Unit testing with Mockito and Maven Surefire

Unit Tests are useful to keep an eye on the components' implementation. The legacy philosophy of Spring promotes reusable components application-wide. The core implementations of these components may either alter states (states of transitory objects) or trigger interactions with other components.

Using Mocks in Unit Tests specifically assesses the behavior of component's methods in regard to other components. When the developer gets used to Mocks, it is amazing to see how much the design becomes influenced toward the use of different layers and logic externalization. Similarly, object names and method names are given more importance. Because they summarize something that is happening elsewhere, Mocks save the energy of the next developer that will have to operate in the area of code.

Developing Unit Tests is by definition an Enterprise policy. As the percentage of code covered by tests can easily reflect the maturity of a product, this code-coverage rate is also becoming a standard reference to assess companies in regard to their products. It must also be noted that companies practicing code reviews as a development process find valuable insights from Pull Requests. When Pull Requests highlight behavioral changes through tests, the impact of potential changes becomes clear faster.

How to do it…

  1. Rerun a Maven Install on the cloudstreetmarket-parent project as in the previous recipe. When the build process comes to build the core module, you should see the following logs that suggest the execution of unit tests during the test phase (between compile and package):
    How to do it…
  2. Those tests can be found in the cloudstreetmarket-core module, specifically in the src/test/java source folder:
    How to do it…

    Both unit tests and integration tests use JUnit:

        <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <version>4.9</version>
        </dependency>
  3. JUnit is natively supported by Eclipse IDE, and this last one offers handles to Run and Debug tests from a class or a method outside Maven:
    How to do it…
  4. A very simple JUnit test class is IdentifiableToIdConverterTest (see the following code). This class asserts that all the registered Entities can be converted by IdentifiableToIdConverter for being Identifiable implementations (remember HATEOAS:):
    import static org.junit.Assert.*;
    import org.junit.Test;
    import edu.zipcloud.cloudstreetmarket.core.entities.*;
    
    public class IdentifiableToIdConverterTest {
    
      private IdentifiableToIdConverter converter;
    
      @Test
      public void canConvertChartStock(){
        converter = new IdentifiableToIdConverter(ChartStock.class);
        assertTrue(converter.canConvert(ChartStock.class));
        }
    
      @Test
      public void canConvertAction(){
        converter = new IdentifiableToIdConverter(Action.class);
        assertTrue(converter.canConvert(Action.class));
      }
    }
  5. More advanced unit tests use the Mockito library. For instance, in the following YahooQuoteToCurrencyExchangeConverterTest:
    @RunWith(MockitoJUnitRunner.class)
    public class YahooQuoteToCurrencyExchangeConverterTest {
      @InjectMocks
      private YahooQuoteToCurrencyExchangeConverter converter;
      @Mock
      private CurrencyExchangeRepository currencyExchangeRepository;
      @Test
      public void transferCriticalData(){
          when(currencyExchangeRepository.findOne(
          any(String.class))
          )
          .thenReturn(new CurrencyExchange("WHATEVER_ID""));
        CurrencyExchange currencyExchange = converter.convert(buildYahooQuoteInstance());
        assertEquals("WHATEVER_ID"",currencyExchange.getId());
        assertEquals("USDGBP=X"", currencyExchange.getName());
        assertEquals(BigDecimal.valueOf(10), 
          currencyExchange.getBid());
        ...
        assertEquals(BigDecimal.valueOf(17), 
        currencyExchange.getOpen());	
        verify(currencyExchangeRepository, times(1))
          .findOne(any(String.class));
      }
      ...
    }

    Here, the highlighted transferCriticalData()test gets an instance of YahooQuoteToCurrencyExchangeConverter that is not initialized with a real @Autowired CurrencyExchangeRepository but instead with a Mock. The converter gets its convert() method invoked with a YahooQuote instance.

    Note

    The Mock is told to return a specific CurrencyExchange instance when its findOne(String s) method is called inside convert(). Then, the returned currencyExchange object is assessed field by field to ensure they are matching their individual expectations.

  6. The following Maven dependency to Mockito has been added across the different modules:
        <dependency>
          <groupId>org.mockito</groupId>
          <artifactId>mockito-all</artifactId>
          <version>1.9.5<version>
        </dependency>
  7. A more extended use of Mockito for unit tests can be found in CommunityServiceImplTest. For example, in the following example, the registerUser_generatePasswordAndEncodeIt test makes use of the ArgumentCaptor:
    @Test
    public void registerUser_generatesPasswordAndEncodesIt() {
      when(communityServiceHelper.generatePassword())
        .thenReturn("newPassword");
      when(passwordEncoder.encode("newPassword"))
        .thenReturn("newPasswordEncoded");
      ArgumentCaptor<User>userArgumentCaptor = 
        ArgumentCaptor.forClass(User.class);
      userA.setPassword(null);
      communityServiceImpl.registerUser(userA);
      verify(userRepository, times(1))
        .save(userArgumentCaptor.capture());
      verify(passwordEncoder, times(1))
        .encode("newPassword");
      String capturedGeneratedPassword = 
        userArgumentCaptor.getValue().getPassword();
      assertEquals("newPasswordEncoded", capturedGeneratedPassword);
    }

How it works...

@Test annotation

The @Test annotation must be placed on public void methods so that JUnit considers them as test cases. An exception thrown within one of these methods will be considered as a test failure. Consequently, an execution without any exception thrown represents a success.

The @Test annotation can be customized, passing the following two optional arguments.

The expected and timeout arguments

An expected parameter on an @Test annotation specifies that the test is expected to throw a specific type of exception to be successful. When a different Type of exception is thrown or when no exception is thrown at all, JUnit must consider the execution as a failure. When a test case is provided a timeout parameter in its @Test annotation, this test will fail when the execution lasts more than the indicated time.

The @RunWith annotation

As introduced in the recipe, the @RunWith annotation permits the use of external test runners (instead of the default BlockJUnit4ClassRunner coming with JUnit). By the way, a declarative technique for specifying the default JUnit runner could be to get @RunWith targeting JUnit4.class like so: @RunWith(JUnit4.class).

 

A runner runs tests and notifies a RunNotifier of significant events as it does so

 
 --JUnit.org Javadoc

A custom Runner must implement abstract methods from org.junit.runner.Runner such as run(RunNotifier notifier) and getDescription(). It must also follow up on core JUnit functions, driving for example, the test execution flow. JUnit has a set of annotations such as @BeforeClass, @Before, @After, and @AfterClass natively handled by org.junit.runner.ParentRunner. We are going to visit these annotations next.

@Before and @After annotations

In test classes that contains several test cases, it is a good practice to try making the test logic as clear as possible. From this perspective, variable initialization and context reinitialization are operations that people often attempt to externalize for reusability. @Before annotations can be defined on public void methods to get them executed by the Runner before every single test. Similarly, @After annotations mark the public void method again to be executed after each test (usually for cleanup resources or destroying a context).

For information, on inheritance, @Before methods of parent classes will be run before those of the current class. Similarly, @After methods declared in superclasses will be run after those of the current class.

Another interesting point from the Javadoc specifies that all @After methods are guaranteed to run, even if a @Before or a @Test annotated method throws an exception.

@BeforeClass and @AfterClass annotations

The @BeforeClass and @AfterClass annotations can be applied to public static void methods. @BeforeClass causes a method to be run once in the test life cycle. The method will be run before any other @Test or @Before annotated methods.

A method annotated @AfterClass is guaranteed to be run once after all tests and also after all @BeforeClass, @Before, or @After annotated methods even if one of them throws an exception.

@BeforeClass and @AfterClass are valuable tools for handling performance-consuming operations related to the preparation of test context (database connection management and pre/post business treatments).

For information, on inheritance, @BeforeClass annotated methods in superclasses will be executed before the ones of the current class, and @AfterClass annotated methods in the superclasses will be executed after those of the current class.

Using Mockito

Mockito is an Open Source testing framework that supports Test-Driven Developments and Behavior-Driven developments. It permits the creation of double objects (Mock objects) and helps in isolating the system under test.

MockitoJUnitRunner

We have been talking about custom runners. The MockitoJUnitRunner is a bit particular in the way that it implements a decoration pattern around the default JUnitRunner.

Such design makes optional the use of this runner (all the provided services could also be implemented declaratively with Mockito).

The MockitoJUnitRunner automatically initializes @Mock annotated dependencies (this saves us a call to MockitoAnnotations.initMocks(this), in a @Before annotated method for example).

initMocks(java.lang.Object testClass)

 

Initializes objects annotated with Mockito annotations for given testClass: @Mock

 
 --Javadoc

The MockitoJUnitRunner also validates the way we implement the framework, after each test method, by invoking Mockito.validateMockitoUsage().This validation assertively gets us to make an optimal use of the library with the help of explicit error outputs.

The transferCriticalData example

The system under test is the YahooQuoteToCurrencyExchangeConverter. The @InjectMocks annotation tells Mockito to perform injection of dependencies (constructor injection, property setter, or field injection) on the targeted converter using initialized Mocks before each test.

The Mockito.when(T methodCall) method, coupled with thenReturn(T value) allows the definition of a fake CurrencyExchange returned object when a call to currencyExchangeRepository.findOne will actually be made inside the converter.convert(...) tested method.

The Mockito verify method with verify(currencyExchangeRepository, times(1)).findOne(any(String.class)) tells Mockito to validate how the tested convert method has interacted with the Mock(s). In the following example, we want the convert method to have called the repository only once.

The registerUser example

More specifically in the registerUser_generatesPasswordAndEncodesIt test, we make use of a MockitoArgumentCaptor to manually perform deeper analyses on the object that a mocked method has been called with.

A MockitoArgumentCaptor is useful when we don't have an intermediate layer and when results are reused to invoke other methods.

More introspection tools than the superficial (but still very useful) Type checking can be required (for example, any(String.class)). An ArgumentCaptor as a solution is used with extra local variables in test methods.

Tip

Remember that local variables and transitory states in implementation methods will always increase the complexity of their related tests. Shorter, explicit, and cohesive methods are always better options.

There is more…

About Mockito

We advise the Mockito's Javadoc that is very well done and full of practical examples

http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html

JUnit Rules

We didn't cover JUnit Rules in any way so far. JUnit offers @Rule annotations that can be applied on test-class fields to abstract recurring business-specific preparations. It is often used to prepare test context objects (fixtures).

http://www.codeaffine.com/2012/09/24/junit-rules

http://junit.org/javadoc/latest/org/junit/Rule.html

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset