Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mock autowired dependency in JUnit 5 test for Spring Boot 2 app

Consider the following class under test:

public class SomeClass {

    @Autowired
    private SomeDependency someDependency;

    public int inc(int i) {
        someDependency.doSomething();
        return i + 1;
    }

}

How can I mock (preferably with Mockito) someDependency in a JUnit 5 (5.0.1) test for a Spring Boot 2 (2.0.0.M2) application? When I try to invoke SomeClass#inc(int) it yields a NullPointerException because the autowired dependency isn't injected.

like image 867
beatngu13 Avatar asked Oct 16 '17 14:10

beatngu13


People also ask

How do you mock in spring boot test?

To achieve this, we can use the mocking support provided by Spring Boot Test. To check the Service class, we need to have an instance of the Service class created and available as a @Bean so that we can @Autowire it in our test class. We can achieve this configuration using the @TestConfiguration annotation.


1 Answers

Mockito 1 runner (MockitoJUnitRunner class) is not designed to run JUnit 5 tests.

So annotating your class with :

import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class) 
public class MyJUnit5Test {

will have no effect.

To be able to use the @Mock annotation, you could invoke before each test :

 MockitoAnnotations.initMocks(this);

in a method annotated with JUnit 5 @BeforeEach.
But from now a better alternative to not repeat this pre-processing in each JUnit test class is using the MockitoExtension class provided by the mockito-junit-jupiter dependency.


Code example

Supposing the SomeDependency class declared as :

@Component
public class SomeDependency {

    public String returnThat() {
        return "that";
    }
}

You could so mock the dependency in this way in your unit test :

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

import davidhxxx.example.angularsboot.SomeDependency;

@ExtendWith(MockitoExtension.class)    
public class SomeClassTest {

    @Mock
    SomeDependency someDependencyMock;

    private SomeClass someClass;

    @BeforeEach
    public void setup(){
      someClass = new SomeClass(someDependencyMock);
    }

    @Test 
    void myFirstTest() {
      Mockito.when(someDependencyMock.returnThat()).thenReturn("mock result");
      Assertions.assertEquals("mock result", someClass.inc());
    }

}

Note that SomeClass has to provide a way to set its SomeDependency dependency.
You should add a constructor that accepts an instance of.
Using setter is also a way while it provides mutability.


pom.xml requirements

1) You can add the spring-boot-starter-test dependency that includes Mockito and other useful things in inherited dependencies :

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

Add also the junit-jupiter-engine dependency and all other optional JUnit 5 dependencies that you require :

<dependencies>
    ...
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-junit-jupiter</artifactId>
        <version>2.18.0</version> <!-- version to specify as not provided by Spring Boot dependencies -->
        <scope>test</scope>
    </dependency>
</dependencies>

You don't need to specify version for the junit-jupiter-engine artifact but you may need for other JUnit 5 dependencies such as junit-jupiter-params.
Hoping that this would be not required any longer in next versions of Spring Boot.

2) Beware : if you use a Spring Boot version that pulls the 2.20 version of the maven-surefire-plugin it will not work. So you will have to override the maven-surefire-plugin plugin configuration to specify a version compatible with JUnit 5 (that is 2.19 or 2.21 and later).
For example the 2.0.0.M5 version of Spring Boot pulls maven-surefire-plugin:2.20 and so reconfiguring the plugin such as the following is required :

    <plugins>
        ...
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.19</version>
            <dependencies>
                <dependency>
                    <groupId>org.junit.platform</groupId>
                    <artifactId>junit-platform-surefire-provider</artifactId>
                    <version>1.0.0</version>
                </dependency>
            </dependencies>
        </plugin>
     ...
   <plugins>

It will generate a Maven warning :

Overriding managed version 2.20.1 for maven-surefire-plugin

Good news : from the 2.0.1.RELEASE version of Spring Boot this issue is solved as the maven-surefire-plugin version was updated to use 2.21.0 and later version that solves the issue.

like image 65
davidxxx Avatar answered Sep 30 '22 18:09

davidxxx