h1. springtester

The @springtester@ module is used in conjunction with the "spring":http://www.playframework.org/modules/spring module.  Internally, it uses the "Mockito":http://code.google.com/p/mockito/ library and provides additional classes for injecting mocks/stubs when writing tests for your Play! application.

h2. Changes

*Version 2.0:*
* Resolve issue where circular dependency can cause mocks being registered to be null'ed out
* Upgraded the Spring module dependency from 1.0.1 to 1.0.3
* Upgraded the Mockito library dependency from 1.9.0 to 1.9.5

*Version 1.6:*
* Fix which allows mocks that are registered in the test to be resolved in production code using Spring.getBeanOfType(Class).  Previously when mocks were injected into Spring's application context, this would have prevented using Spring.getBeanOfType(class) as it would have returned null (i.e. the only way of resolving dependencies was by using @Resource in classes that were annotated with @Component)

*Version 1.1:*
* Documentation update and fixes

*Version 1.0:*
* Inject resources into functional tests
* Ability to have partial mocks

h2. Getting started

Include @springtester@ in your Play! application by adding the following in your @dependencies.yml@:

bc. - play -> springtester [version]

As @springtester@ depends on the existing Spring Play! module, you will have to include it as well:

bc. - play -> spring [version]

*Note:* @springtester@ has currently only been tested with @version 1.0.1@ of the @spring@ module.

Then update your project's dependencies to include @springtester@ and its dependencies:

bc. play deps

h2. Usage

h3. Standard Mocks

The @springtester@ module assumes you are wiring your application using the @spring@ module.  Here is an example of two classes being wired up using the @spring@ module.  The first is a @Person@ class that injects an @AgeCalculator@ and calls the @calculate(...)@ method:

bc. @Component
public class Person {
    // Here we are injecting the AgeCalculator into the Person class
    @Resource private AgeCalculator ageCalculator;
    public boolean canVote() {
        int age = calculator.calculate("1-January-1980");
        return age >= 18;
    }
}

Note that person itself is annotated with the @Component@ annotation.  The @AgeCalculator@ will then take birth date of the @Person@ and calculate their age:

bc. @Component
public class AgeCalculator {
    public int calculate(String birthDate) {
        // some code that figures out the age of the person
    }
}

Now let's say you want to write a test for @Person@ called @PersonTest@ and wish to replace the @AgeCalculator@ with a mock that returns the age of 20 instead of calculating the actual age of the @Person@:

bc. public final class PersonTest extends SpringMockitoUnitTestCase {
    @Subject private Person subject;
    @Mock(name = "ageCalculator") AgeCalculator mockAgeCalculator;
    @Test
    public void testCanVote() throws Exception {
        Mockito.when(mockAgeCalculator.calculate("1-January-1980")).thenReturn(20);
        boolean actual = subject.canVote();
        assertEquals(true, actual);
    }
}

*How it works:*

* The @PersonTest@ extends @SpringMockitoUnitTestCase@.  This superclass does all the magic to inject mocks into your objects.

* The annotation @Subject@ will auto-magically inject the subject into the test.  In the example above, a @Person@ object will be injected into the test.  Be sure that the subject is a Spring component by annotating it with @Component.

* The annotation @Mock@ (provided by @Mockito@) is used to auto-magically create a mock of the given class.  In the example above, an @AgeCalculator@ object will be mocked and set against the field @mockAgeCalculator@.  The @Mock@ annotation can take the @name@ property which specifies the name of the field the mock will replace in the subject.  If the field name of the object to be mocked is the same as the field name in the subject, you may omit the name property like so:

bc. // The name of the mock "ageCalculator" has the same name as the
// field "ageCalculator" found in the Person class and therefore
// does not require the "name" property.
@Mock AgeCalculator ageCalculator;

* The remainder of the example test above uses @Mockito@ to mock the call to the @calculate@ method and return 20 and then asserts that the @subject.canVote()@ call will return true (since 20 is older than or equal to 18).

* The class @SpringMockitoUnitTestCase@ extends @DatabaseUnitTestCase@ which provides the helper method @checkCount(Class<? extends Model> cls, long expected)@ which can be used to check the row count of table.

* The @DatabaseUnitTestCase@ will also delete the test in-memory database before each test method to ensure a clean run for each test method execution.

h3. Partial Mocks

Similar to the annotation @Mock@, you can also annotate using the annotation @PartialMock@.  This annotation exposes the partial mocking (i.e. spy) feature from Mockito.  In the example below, we are stubbing a call to sport().  Any calls to number() will call the "real" code.

bc. public final class PersonTest extends SpringMockitoUnitTestCase {
    @Subject private Person subject;
    @PartialMock(name = "favouriteThings") FavouriteThings partialMockFavouriteThings;
    @Test
    public void testSayFavouriteThings() throws Exception {
        // Assuming sport() would normally return "football", the follow statement will ensure that "tennis" is returned instead.
        // All other methods found on the partialMockFavouriteThings object will be called as normal (i.e. not stubbed out).
        Mockito.when(partialMockFavouriteThings.sport()).thenReturn("tennis");
        ...
    }
}

*Note:* Be sure to read the "gotcha's":http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html#13 on the official Mockito site when spying on real objects (partial mocks)

h3. Functional Testing

* You can also inject mocks into Play's FunctionalTesting framework by extending the class @SpringMockitoFunctionalTestCase@.  In the case of functional tests, you do not need to specify a subject.  In the example below, the test will make an HTTP request and all instances of AgeCalculator will be replaced with a mock:

bc. public class FunctionalExampleTest extends SpringMockitoFunctionalTestCase {
    @Mock(name = "ageCalculator") AgeCalculator mockAgeCalculator;
    @Test
    public void testSomething() {
        // Do some mockito expectations
        // This URL will hit a controller that will eventually inject the mocked AgeCalculator
        Http.Response response = GET("/some/url");
        // Verify response and expectations
    }
}

* Sometimes you may want to inject mocks for certain test methods but not for other tests in the class.  In that situation you can create your own mock (or any object you like) and inject it into Spring's ApplicationContext like this:

bc. @Test
public void testSomeTestMethod() {
    // Create and register the mock object for just this test method
    AgeCalculator mockAgeCalculator = Mockito.mock(AgeCalculator.class);
    registerObject("ageCalculator", mockAgeCalculator);
    // Do some mockito expectations
    // Make your calls for testing
    // Verify response and expectations
}

* You can inject resources into your tests.  So if your test requires to use some component called @SomeObject@, then you can do this:

bc. public class ExampleTest extends SpringMockitoUnitTestCase {
    @Resource private SomeObject someObject;
    @Test
    public void testSomething() {
        someObject.someMethod(...);
        ...
    }
}

h2. Source Code

If you wish to play with the source, please download the code "here":https://github.com/digiarnie/springtester

To set up your project for IntelliJ just run these commands in the root @springtester@ directory:

* play deps --sync
* play idealize
* open the @springtester@ project in IntelliJ
* Make the @src@ directory a source root
