Spring Unit Testing with Mockito For Beginners

Spring Unit Testing with Mockito For Beginners

·

14 min read

Spring Unit Testing with Mockito For Beginners

This tutorial is part of Springing into Action: A Spring Boot Journey from Novice to Pro Series, be sure to check it out for more related content!

Spring provides several powerful features for unit testing, including the ability to easily create mock objects and integrate them into the application context. In this article, we will explore how to use Mockito, a popular mocking framework, in conjunction with Spring to write effective unit tests.

Setup

To get started, you will need to add the following dependency to your project's build file:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-starter-test</artifactId>
    <version>2.4.0</version>
    <scope>test</scope>
</dependency>

This spring-starter-test dependency includes a number of useful libraries for testing Spring-based applications, including JUnit, Hamcrest, and Mockito.

Introduction to Unit Testing

Unit testing is a software testing method in which individual units or components of a software application are tested in isolation from the rest of the system. The goal of unit testing is to validate that each unit of the software application is working as intended.

Unit tests are typically written by developers and are used to test the functionality of small, isolated components of the application such as methods or classes. Unit tests are automated and are run frequently during development to ensure that changes to the code do not break existing functionality.

Benefits of Unit Testing

  • Early detection of bugs and errors in the code.

  • Increased confidence in the code and the ability to make changes without fear of introducing new bugs.

  • Improved code quality and design.

  • Faster development times as developers can quickly and easily make changes to the code.

Unit testing is just one type of testing and it's not enough to ensure that the whole application is working as expected. There are other types of testing as well, such as:

  • Integration testing: testing how different units or components of the application work together.

  • Functional testing: testing the functionality of the application from the user's perspective.

  • Acceptance testing: testing the application to ensure that it meets the requirements of the end user.

In general, unit testing is the foundation of software testing and provides a good starting point for more comprehensive testing. It's important to have a good unit testing strategy in place to ensure that the application is working as intended and to reduce the risk of bugs and errors.

Unit testing is an important part of software development and it's beneficial to have a good understanding of how to write and execute unit tests. Spring Boot provides a convenient way to write unit tests with its built-in support for testing, along with the tools such as JUnit, Mockito, and Spring Test.

Understanding Mockito Framework

Mockito is a popular open-source testing framework for Java that allows developers to create mocks and stubs for their unit tests. It is designed to make it easy to write tests for your code by providing a simple, clean, and intuitive API. One of the key features of Mockito is its ability to create mocks and stubs of objects, which are used to simulate the behavior of real objects in a test environment.

Mocks

A mock object is a simulation of a real object that is used in place of the real object in a test environment. In Mockito, a mock object can be created using the mock method or the @Mock annotation. The mock method creates a mock object of a specific class, while the @Mock annotation creates a mock object of a field that is annotated with the @Mock annotation.

For example, we can create a mock of the MyService class like this:

MyService myService = mock(MyService.class);

or

@Mock
MyService myService;

Spies

A spy object, on the other hand, is a special type of mock object that allows you to call the real methods of the object while still being able to specify the behavior of certain methods. In Mockito, a spy object can be created using the spy method or the @Spy annotation. The spy method creates a spy object of a specific class, while the @Spy annotation creates a spy object of a field that is annotated with the @Spy annotation.

For example, we can create a spy of the MyService class like this:

MyService myService = spy(new MyService());

or

@Spy
MyService myService = new MyService();

Stubs

A stub is an object that you can use to simulate the behavior of a real object in a test environment. In Mockito, a stub can be created using the when method. The when method is used to specify the behavior of a method for a specific input.

For example, we can use the when method to specify the behavior of the isValid method for a specific input:

when(myService.isValid("valid")).thenReturn(true);

Differences between @MockBean, @SpyBean and @Stub

@MockBean is a Spring annotation that creates a mock of a bean and adds it to the Spring context. This means that the mock object will be used in place of the real object when it is injected into other beans.

@SpyBean is also a Spring annotation that creates a spy of a bean and adds it to the Spring context. This means that the spy object will be used in place of the real object when it is injected into other beans, but it will still call the real methods of the object.

@Stub is not a Spring annotation it is a term used to describe the use of the when method from the Mockito framework, which is used to specify the behavior of a method for a specific input.

In general, @MockBean and @SpyBean are used to create mocks and spies of objects that are used in the Spring context, while the when method is used to create stubs of methods for specific inputs. It's important to note that the usage of these annotations and methods depend on the specific needs of your test case and the behavior you want to simulate.

For example, if you want to test the behavior of a service class that makes a call to a repository class, you might use a @MockBean annotation to create a mock of the repository class so that you can control its behavior in the test. On the other hand, if you want to test the behavior of a service class that makes a call to a external API, you might use the when method to create a stub of the API call and control its behavior in the test.

In summary, the key difference between using @MockBean, @SpyBean and when method is that @MockBean and @SpyBean are used to create mocks and spies of objects that are used in the Spring context, while the when method is used to create stubs of methods for specific inputs.

Configuration Class for SpringBootTest

In addition to the annotations and methods discussed above, Spring also provides a way to configure the test context through the use of configuration classes. These classes can be used to specify which beans should be loaded for the test and which should be excluded.

For example, the following configuration class can be used to only load the beans that are needed for the test and exclude the others:

@Configuration
@Import({ Service.class, Repository.class })
public class TestConfig {

}

This configuration class can then be used in the test class as follows:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestConfig.class)
public class MyTest {
    ...
}

This way we can include only the classes that are needed for the test and exclude the others. This can be especially useful when we have a large application and we only want to test a specific part of it.

It is also possible to specify the configuration class while using @DataJpaTest, @WebMvcTest, @JsonTest and other annotations that provide a slice of the context.

@WebMvcTest(controllers = MyController.class, config = TestConfig.class)

This is particularly useful when you want to test only specific layers of your application, such as the web layer or the data access layer.

Spring Starter Test Usage

One of the most powerful features of Spring for unit testing is the ability to easily create mock objects and integrate them into the application context. We can use the @MockBean annotation to create a mock of a bean and automatically add it to the application context. For example:

@MockBean
private MyService myService;

In this example, a mock of the MyService bean is created and automatically added to the application context. We can then use this mock to write test cases and verify that the correct methods are called with the correct arguments.

We can also use the @SpyBean annotation to create a spy of a bean instead of a mock. Spies are useful when we want to test the behavior of a bean while still calling the real methods.

The @TestExecutionListeners annotation can be used to specify one or more test execution listeners that will be used for the test class. The DependencyInjectionTestExecutionListener is one of the built-in listeners provided by Spring and it can be used to control the order of test methods.

For example, we can use the @Dependencies annotation to specify the order in which methods should be executed:

@TestExecutionListeners(DependencyInjectionTestExecutionListener.class)
@Dependencies({ TestMethod1.class, TestMethod2.class })
public class Test {
    // test methods here

    @Test
    public void testMethod1() {
        // test code here
    }

    @Test
    public void testMethod2() {
        // test code here
    }
}

In the example above, we are using the @TestExecutionListeners annotation to specify that the DependencyInjectionTestExecutionListener will be used for the test class. This listener can be used to control the order of test methods, for example by specifying the @Dependencies annotation.

To test with the context re-usage, we can use the @DirtiesContext annotation. This annotation can be used to indicate that a test method has dirtied the context, and that the context should be rebuilt before the next test method is executed. For example:

@Test
@DirtiesContext
public void testMethod() {
    // test code here
}

In this example, the context will be rebuilt before the next test method is executed.

There are also ways to control the order of test classes and methods, for example using the @TestPropertySource annotation. This annotation can be used to specify properties that should be used for a test class or method. For example:

@TestPropertySource(properties = { "property1=value1", "property2=value2" })
public class Test {
    // test methods here
}

In this example, the properties property1 and property2 will be set to value1 and value2 respectively for all the test methods in this class.

Another way to control the order of test classes and methods is by using the @TestMethodOrder annotation. This annotation can be used to specify the order in which test methods should be executed. For example:

@TestMethodOrder(OrderAnnotation.class)
public class Test {
    // test methods here

    @Order(1)
    @Test
    public void testMethod1() {
        // test code here
    }

    @Order(2)
    @Test
    public void testMethod2() {
        // test code here
    }
}

In this example, the testMethod1 will be executed before the testMethod2 because it is annotated with @Order(1) and testMethod2 is annotated with @Order(2).

Code Examples

Here is an example of a simple unit test using Mockito and Spring:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyServiceTest {

    @MockBean
    private MyRepository repository;

    @Autowired
    private MyService service;

    @Test
    public void testGetData() {
        when(repository.findById(1L)).thenReturn(new MyData(1L, "Test Data"));
        MyData data = service.getData(1L);
        assertEquals(1L, data.getId());
        assertEquals("Test Data", data.getValue());
        verify(repository).findById(1L);
    }
}

In this example, we are using the @MockBean annotation to create a mock of the MyRepository bean and @Autowired annotation to inject the MyService bean into the test class. We are then using the when method from Mockito to specify the behavior of the mock repository and using the verify method to check that the findById method is called with the correct argument.

Here is an example of a unit test using @SpyBean and @DirtiesContext:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyServiceTest {

    @SpyBean
    private MyService service;

    @Test
    public void testDoSomething() {
        doReturn(true).when(service).isValid();
        service.doSomething();
        verify(service).isValid();
        verify(service).saveData();
    }

    @Test
    @DirtiesContext
    public void testDoSomethingElse() {
        doReturn(false).when(service).isValid();
        service.doSomething();
        verify(service, never()).saveData();
    }
}

In this example, we are using the @SpyBean annotation to create a spy of the MyService bean, which allows us to test the behavior of the bean while still calling the real methods. In the testDoSomething method, we are using the doReturn method from Mockito to specify the behavior of the isValid method and then verifying that the saveData method is called. In the testDoSomethingElse method, we are also using the doReturn method to specify the behavior of the isValid method, but this time we are expecting the saveData method to not be called. We are also using the @DirtiesContext annotation on this method to indicate that the context should be dirtied after this test method is executed. This means that any changes made to the context during this test method will not affect the other test methods.

Mocking Static Methods in Classes for Tests

In some cases, we may need to test a class that has static methods or a static class itself. Mocking static methods can be a bit tricky as mockito does not support mocking of static methods by default, but it can be done by using the Mockito.mock() with Mockito.spy() .

Here is an example of how to use Mockito.spy() to mock a static method:

public class MyTest {
    @Test
    public void testStaticMethod() {
        MyStaticClass spy = Mockito.spy(MyStaticClass.class);
        Mockito.doReturn("mocked result").when(spy).staticMethod();
        String result = spy.staticMethod();
        assertEquals("mocked result", result);
    }
}

In this example, we use the Mockito.spy() method to create a spy of the static class, and the Mockito.doReturn() method is used to specify the behavior of the static method.

It's worth noting that using Mockito.spy() is not recommended in some cases as it's not always recommended to test static methods and classes because in some cases it might be better to refactor the code and make it testable by using dependency injection and interfaces.

Parallel Execution of Tests

Parallel execution of tests is a way to speed up the testing process by running multiple tests at the same time. This can be achieved in Spring Boot by configuring the properties file and specifying the number of threads to use for running tests.

Parallel Execution within the Same Class

To run tests in parallel within the same class, we can use the @Test(threadPoolSize = X, invocationCount = Y) annotation. threadPoolSize represents the number of threads to use for running the tests, and invocationCount represents the number of times the test method should be invoked. For example, if we have a test class with 4 test methods and we want to run them in parallel using 2 threads, the configuration would look like this:

@Test(threadPoolSize = 2, invocationCount = 4)
public void testMethod() {
    // test logic here
}

Parallel Execution Across Classes

To run tests in parallel across different classes, we can use the @Test(threadPoolSize = X) annotation at the class level. This will run all the test methods within the class in parallel using the specified number of threads. For example, if we have 2 test classes with 4 test methods each and we want to run them in parallel using 2 threads, the configuration would look like this:

@Test(threadPoolSize = 2)
public class TestClass1 {
    @Test
    public void testMethod1() {
        // test logic here
    }
    @Test
    public void testMethod2() {
        // test logic here
    }
    // additional test methods
}

@Test(threadPoolSize = 2)
public class TestClass2 {
    @Test
    public void testMethod1() {
        // test logic here
    }
    @Test
    public void testMethod2() {
        // test logic here
    }
    // additional test methods
}

You can also use the spring.test.parallel.execution.enabled and spring.test.parallel.execution.strategy in the application.properties file to configure the parallel execution.

spring.test.parallel.execution.enabled=true
spring.test.parallel.execution.strategy=classes

The strategy property can take values of classes and methods. classes runs all the test classes in parallel and methods runs all the test methods within a class in parallel.

It's worth noting that parallel execution may cause some test methods to fail due to the shared state or unexpected order of test execution. Therefore, it's important to make sure that your test methods are independent and not dependent on the execution order.

Conclusion

In conclusion, Spring unit testing with Mockito is a powerful combination for testing Spring applications. We have covered various features and annotations such as @MockBean, @SpyBean, and @TestExecutionListeners that allow us to fine-tune our tests and ensure that they are accurate and reliable. Additionally, we discussed the differences between mock, spy, and stub annotations and the usage scenarios for each.

Moreover, we also covered the configuration class for SpringBootTest and how to specify only some classes to run for testing and not the whole context, this is useful when we want to test a specific set of classes and not all the classes in the application. Also, we discussed how to mock static methods in classes for tests and testing static classes with code examples.

Finally, we discussed the importance of unit testing and its benefits, including the different types of testing such as unit tests and integration tests. Additionally, we covered the parallel execution of tests in the same class or parallel execution of tests across classes using the properties file in SpringBootTests and the differences and configurations needed with examples in code and execution time.

In summary, Spring unit testing with Mockito is a powerful tool for ensuring the reliability and accuracy of your Spring applications. With the various features and annotations provided by Spring and Mockito, you can fine-tune your tests to suit your specific needs and ensure that your application is working as expected.