Hot questions for Using Mockito in spring boot test

Top 10 Java Open Source / Mockito / spring boot test

Question:

I have just migrated to JDK 11 in order to use the latest Java LTS version. If I change the execution JRE in Eclipse from 10 to 11 (and only then), when I try to run my tests, I get the following exception stacktrace.

Please note that everything works as expected if I switch back to jdk-10. I use Spring Boot and my tests also use Mockito. My pom.xml is also shown here.

Is there a known incompatibility between Spring Boot and/or Mockito with jdk-11? Perhaps another reference missing in pom.xml?

pom.xml:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>2.3.0.1</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>javax.transaction</groupId>
            <artifactId>jta</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-model</artifactId>
            <version>3.5.4</version>
        </dependency>
        <dependency>
            <groupId>org.modelmapper</groupId>
            <artifactId>modelmapper</artifactId>
            <version>2.2.0</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <!-- update Hibernate dependency on Javassist to 3.23.1 for Java 11 compatibility -->
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.23.1-GA</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <executable>true</executable>
                    <skipTests>true</skipTests>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-releases</id>
            <url>https://repo.spring.io/libs-release</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-releases</id>
            <url>https://repo.spring.io/libs-release</url>
        </pluginRepository>
    </pluginRepositories>

Stacktrace:

java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125)
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108)
    at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)
    at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:246)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runners.Suite.runChild(Suite.java:128)
    at org.junit.runners.Suite.runChild(Suite.java:27)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
Caused by: org.mockito.exceptions.base.MockitoException: 
Mockito cannot mock this class: class org.foo.MockedClass.

Mockito can only mock non-private & non-final classes.
If you're not sure why you're getting this error, please report to the mailing list.


Java               : 11
JVM vendor name    : Oracle Corporation
JVM vendor version : 11+28
JVM name           : Java HotSpot(TM) 64-Bit Server VM
JVM version        : 11+28
JVM info           : mixed mode
OS name            : Linux
OS version         : 4.4.0-128-generic


Underlying exception : java.lang.UnsupportedOperationException: Cannot define class using reflection
    at org.springframework.boot.test.mock.mockito.MockDefinition.createMock(MockDefinition.java:157)
    at org.springframework.boot.test.mock.mockito.MockitoPostProcessor.createMock(MockitoPostProcessor.java:222)
    at org.springframework.boot.test.mock.mockito.MockitoPostProcessor.registerMock(MockitoPostProcessor.java:192)
    at org.springframework.boot.test.mock.mockito.MockitoPostProcessor.register(MockitoPostProcessor.java:174)
    at org.springframework.boot.test.mock.mockito.MockitoPostProcessor.postProcessBeanFactory(MockitoPostProcessor.java:144)
    at org.springframework.boot.test.mock.mockito.MockitoPostProcessor.postProcessBeanFactory(MockitoPostProcessor.java:131)
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:282)
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:170)
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:694)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:532)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:780)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:412)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:333)
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:139)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)
    ... 33 more
Caused by: java.lang.UnsupportedOperationException: Cannot define class using reflection
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection$Dispatcher$Unavailable.defineClass(ClassInjector.java:821)
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection.inject(ClassInjector.java:185)
    at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$Default$InjectionDispatcher.load(ClassLoadingStrategy.java:187)
    at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:79)
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:4457)
    at org.mockito.internal.creation.bytebuddy.SubclassBytecodeGenerator.mockClass(SubclassBytecodeGenerator.java:121)
    at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator$1.call(TypeCachingBytecodeGenerator.java:37)
    at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator$1.call(TypeCachingBytecodeGenerator.java:34)
    at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:138)
    at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:346)
    at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:161)
    at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:355)
    at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.mockClass(TypeCachingBytecodeGenerator.java:32)
    at org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker.createMockType(SubclassByteBuddyMockMaker.java:71)
    at org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker.createMock(SubclassByteBuddyMockMaker.java:42)
    at org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker.createMock(ByteBuddyMockMaker.java:25)
    at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:35)
    at org.mockito.internal.MockitoCore.mock(MockitoCore.java:65)
    at org.mockito.Mockito.mock(Mockito.java:1855)
    ... 49 more
Caused by: java.lang.IllegalStateException: Could not find sun.misc.Unsafe
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingUnsafe$Dispatcher$Disabled.initialize(ClassInjector.java:1366)
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingUnsafe.inject(ClassInjector.java:1202)
    at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$ForUnsafeInjection.load(ClassLoadingStrategy.java:458)
    at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:79)
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:4457)
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection$Dispatcher$Indirect.make(ClassInjector.java:684)
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection$Dispatcher$CreationAction.run(ClassInjector.java:302)
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection$Dispatcher$CreationAction.run(ClassInjector.java:290)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection.<clinit>(ClassInjector.java:70)
    at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$Default$InjectionDispatcher.load(ClassLoadingStrategy.java:184)
    ... 65 more
Caused by: java.lang.NoSuchMethodException: sun.misc.Unsafe.defineClass(java.lang.String, [B, int, int, java.lang.ClassLoader, java.security.ProtectionDomain)
    at java.base/java.lang.Class.getMethod(Class.java:2109)
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingUnsafe$Dispatcher$CreationAction.run(ClassInjector.java:1269)
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingUnsafe$Dispatcher$CreationAction.run(ClassInjector.java:1257)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingUnsafe.<clinit>(ClassInjector.java:1136)
    ... 74 more

Answer:

Was the same problem. Add Mockito 2.22.0 to dependencies. Everything seems to work.

compile group: 'org.mockito', name: 'mockito-core', version: '2.22.0'

Question:

how to write a mockito test case for if...else statement which also includes exception test, I am quite confused about this. UserService is an interface

UserFactory.java

public class UserFactory {
    @Autowired
    private List<UserService> UserList;

    private final Map<String, UserService> UserMap = new HashMap<>();

    @PostConstruct
    public void initUserMap() {
        for (final UserService user : UserList) {
            UserMap.put(user.getUserId(), user);
        }
    }

    public UserService getUser(String userId) {
        final UserService userService = UserMap.get(userlId);
        if (null == userService) {
            throw new IllegalArgumentException("user are not in the group");
        }
        return userService;
    }
}

UserService(Interface)

public interface UserService{
  String getUserName();
  String getUserId();
}

here is my test, but it is not correct...

@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {

    @Mock
    private UserService userService;

    @InjectMocks
    private UserFactory UserFactory;

    private Map<String, User> UserMap;

    private String userId = "123";

    @Before
    public void set_up(){
        UserMap = new HashMap<>();
        UserMap.put(userId, userService);
    }

    @Test
    public void getUser(){
        when(UserMap.get(userId)).thenReturn(userService);
        userService actual = userServiceFactory.getUser(userId);
        assertEquals(UserMap.get(userId), actual);
    }
}

Answer:

1) You write an unit test, not a sliced test or an integration test. So here Spring Boot doesn't matter as you don't need to load a container to test the logic of your component. You can read my question/answer about this field if you are interested in.

2) A branch (if/else) in the logic means that you have multiple scenarios. Distinct scenarios mean generally distinct test methods and meaningful names. You can also rely on the given/when/then idiom.

3) Since in your test the input information for the branch is provided by the mock, it also means that you will register distinct behaviors for the mock in each test method.

4) UserService doesn't need to be mocked. It has to be a value returned by the mock not the mock itself. Here you have to mock the Map.

5) You test UserFactory, so you have to name it UserFactoryTest .

For example :

@RunWith(MockitoJUnitRunner.class)
public class UserFactoryTest {

    @Mock
    private Map<String, User> UserMap;

    @InjectMocks
    private UserFactory UserFactory;

    private String userId = "123";


    @Test
    public void getUser(){
       when(UserMap.get(userId)).thenReturn(userService);
       userService actual = userServiceFactory.getUser(userId);
       assertEquals(UserMap.get(userId), actual);
    }


    @Test
    public void getUser_with_unknown_userId(){
        Assertions.assertThrows(IllegalArgumentException.class, 
                            ()->  userServiceFactory.getUser(userId));
    }

}

You notice that in the second case I don't register any behavior for the mock. By default Mockito will return null and actually it is what I need to provoke the exception. So it is fine. Note also that I wrote the assertion by relying on the JUnit 5 library and not the JUnit 4 library that you seem to use according the used runner. You should consider to switch to JUnit 5 for new tests.

Question:

My Service class is below, followed by its test -

@Service
public class MyServiceImpl implements MyService {

        @Autowired
        private RestTemplate restTemplate;

        @Override
        public StudentInfo getStudentInfo(String name) {
            HttpHeaders headers = new HttpHeaders();
            headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);

            HttpEntity entity = new HttpEntity(headers);

            StudentInfo student = null;

            URI uri = new URI("http:\\someurl.com");             

           ResponseEntity<String> responseEntity = restTemplate.exchange(uri,
                        HttpMethod.GET, entity,
                        String.class);

           if (responseEntity.getStatusCode().equals(HttpStatus.NO_CONTENT)) {
                   throw new Exception("Student absent");
            }else {
              ObjectMapper mapper = new ObjectMapper();
              StudentInfo student = mapper.readValue(responseEntity.getBody(), StudentInfo.class);

           }

            return student;
        }
    }

Test class: In my test class below, I see ResponseEntity object as null while debugging which causes a NPE.

@RunWith(MockitoJUnitRunner.class)
public class MyServiceImplTest {

    @InjectMocks
    private MyService service = new MyServiceImpl();

    @Mock
    private RestTemplate restTemplate;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testStudentGetterResponse() {

        ResponseEntity<String> mockEntity = Mockito.spy(new ResponseEntity({"id" : 1, "name" : "Rutzen"}, HttpStatus.OK));

        doReturn(mockEntity).when(restTemplate).exchange(any(URI.class), any(HttpMethod.class), any(ResponseEntity.class),
                any(Class.class));

        StudentInfo info = service.getStudentInfo("testuser");

        Assert.assertNotNull(info);


    }

}

When I debug the test, I get a null value for responseEntity at the following line in the main service class -

 ResponseEntity<String> responseEntity = restTemplate.exchange(uri,
                        HttpMethod.GET, entity,
                        String.class);

Answer:

This instruction ...

doReturn(mockEntity).when(restTemplate).exchange(
    any(URI.class), 
    any(HttpMethod.class), 
    any(ResponseEntity.class),              
    any(Class.class)
);

... should be replaced with:

doReturn(mockEntity).when(restTemplate).exchange(
    any(URI.class), 
    any(HttpMethod.class), 
    any(HttpEntity.class),              
    any(Class.class)
);

Because getStudentInfo() creates an instance of HttpEntity (not ResponseEntity) which is then passed to the restTemplate.exchange() invocation.

Question:

How mock 2 beans when one is into second ?

public class A {
...
}

public class B {
private A a;
}

I tried:

@MockBean 
private A a;

@InjectMocks 
private B b;

@Before
public void executedBeforeEach() {
MockitoAnnotations.initMocks(this);
}

but had exception:

org.mockito.exceptions.base.MockitoException: 
Cannot instantiate @InjectMocks field named 'B'.
You haven't provided the instance at field declaration so I tried to     construct the instance.
However, I failed because: the type 'B' is an interface.

spring version:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.8.RELEASE</version>
    <relativePath/>
</parent>

test dependency:

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

how to do it right ?where did I make a mistake ?


Answer:

You just need to autowire B. By using the annotation @MockBean you are telling the test Spring context to replace the actual bean of type A with a mock and this will automatically be injected into everywhere A in included (i.e. in your B bean).

@MockBean 
private A a;

@Autowire
private B b;

This is under the assumption that you are annotating your test class with @SpringBootTest

Question:

Good Afternoon to the experts,

I have a requirement where I will be invoking sequential calling of 3 REST APIs as part of a single client call GET /offers to retrieve offers available for each product in different aisles of a department store as below

  1. Get all the aisles in a department store /aisels
  2. Get all the products in an aisle /aisles/{aisleID}/products
  3. Get all the offers for the product /product/{productId/offers

    To do this from my @Service class using the RestTemplate exchange method:

    ResponseEntity aisles= restTemplate.exchange(url, HttpMethod.GET, requestEntity, Aisles.class);

Then retrieve each aisleId in a loop and invoke the 2nd API to get the Products

ResponseEntity<Products> products= restTemplate.exchange(url,
                    HttpMethod.GET, requestEntity, Products.class);

Then retrieve each productId in a loop and invoke the 3rd API to get the Offers

Finally collate all the responses to send the list of offers to the client.

Now, I am new to mockito framework for writing the JUnits. And my service class got a single method named retrieveAllOffers() in which I have the 3 exchange methods as above.

I was wondering how could I mock these 3 calls in my Junit to get a successful response scenario.

Your help is highly appreciated.


Answer:

Instead of mocking the rest template you can mock the response of the service only. To do that you can use Wiremock ( http://wiremock.org/) which provides an api to stub calls on specific URLS and mock their responses to whatever you like and verify that they were called.

For your case you would need 3 stubs, one for each exchange. For example for this method restTemplate.exchange(url, GET, requestEntity, Products.class); that you provided a stub would look like

stubFor(get(urlEqualTo("yourUrl"))
  .willReturn(aResponse()
    .withStatus(200)
    .withBody(new ObjectMapper().writeValueAsString(yourResponseObject)
    .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE))

note that your url will need to point its server where wiremock is running ( if you use the java dependency and junit rule it will be localhost:wiremockPort )

the setup is pretty straight forward as you need to add a single rule to your test

@Rule public WireMockRule wireMockRule = new WireMockRule(options().dynamicPort()); have a look here for the configure of the rule http://wiremock.org/docs/configuration/

and in the end you verify that your stubs where called with

verify(getRequestedFor(urlEqualTo("/yourUrl"))
  .withRequestBody(equalTo(new ObjectMapper().writeValueAsString(theObjectThatShouldBePosted))));

Hope that helped!