0

I am new to backend testing and I am testing my spring boot app with MockMvc. However, when I want to test my Post request, I have the error mentioned in the title when I use

verify(userService,atLeastOnce()).addUser(user1);

The error log is as follows:

{"id":1,"name":"Burakhan Aksoy","email":"burak@burak.com","gender":"Male","phoneNumber":"12321"}

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /users
       Parameters = {}
          Headers = [Content-Type:"application/json;charset=UTF-8", Content-Length:"96"]
             Body = {"id":1,"name":"Burakhan Aksoy","email":"burak@burak.com","gender":"Male","phoneNumber":"12321"}
    Session Attrs = {}

Handler:
             Type = io.thebman.restapitesting.controllers.UserController
           Method = io.thebman.restapitesting.controllers.UserController#addUser(User)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 201
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"id":1,"name":"Burakhan Aksoy","email":"burak@burak.com","gender":"Male","phoneNumber":"12321"}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []


Argument(s) are different! Wanted:
io.thebman.restapitesting.service.UserService#0 bean.addUser(
    io.thebman.restapitesting.view.User@1b5a1d85
);
-> at io.thebman.restapitesting.UserControllerTest.postUserThenValidateUserAddedSuccessfully(UserControllerTest.java:119)
Actual invocations have different arguments:
io.thebman.restapitesting.service.UserService#0 bean.addUser(
    io.thebman.restapitesting.view.User@31e130bf
);
-> at io.thebman.restapitesting.controllers.UserController.addUser(UserController.java:32)

Comparison Failure: 
<Click to see difference>

Argument(s) are different! Wanted:
io.thebman.restapitesting.service.UserService#0 bean.addUser(
    io.thebman.restapitesting.view.User@1b5a1d85
);
-> at io.thebman.restapitesting.UserControllerTest.postUserThenValidateUserAddedSuccessfully(UserControllerTest.java:119)
Actual invocations have different arguments:
io.thebman.restapitesting.service.UserService#0 bean.addUser(
    io.thebman.restapitesting.view.User@31e130bf
);
-> at io.thebman.restapitesting.controllers.UserController.addUser(UserController.java:32)

    at io.thebman.restapitesting.UserControllerTest.postUserThenValidateUserAddedSuccessfully(UserControllerTest.java:119)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at java.util.ArrayList.forEach(ArrayList.java:1259)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at java.util.ArrayList.forEach(ArrayList.java:1259)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)

My UserController class is as follows:

package io.thebman.restapitesting.controllers;

import io.thebman.restapitesting.service.UserService;
import io.thebman.restapitesting.view.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("/users/{id}")
    public User getUserById(@PathVariable int id){

        return userService.getUser(id);
    }

    @RequestMapping("/users")
    public List<User> getUsers(){
        return userService.getUsers();
    }

    @PostMapping("/users")
    public @ResponseBody
    ResponseEntity<User> addUser(@RequestBody User user){
        return new ResponseEntity<User>(userService.addUser(user), HttpStatus.CREATED);
    }
}

My UserControllerTest class as follows:

package io.thebman.restapitesting;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.thebman.restapitesting.controllers.UserController;
import io.thebman.restapitesting.service.UserService;
import io.thebman.restapitesting.view.User;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatchers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.ContentResultMatchers;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;

import static org.hamcrest.Matchers.*;

import java.util.Arrays;
import java.util.List;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@WebMvcTest(UserController.class)
public class UserControllerTest {
    @Autowired
    private UserController userController;

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    //Test /users (POST)
    @Test
    public void postUserThenValidateUserAddedSuccessfully() throws Exception {
        User user1 =new User(1,"Burakhan Aksoy",
                "burak@burak.com","Male",
                "12321");

        when(userService.addUser(any(User.class))).thenReturn(user1);

        //Start mocking http request
         mockMvc.perform(MockMvcRequestBuilders.post("/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(asJsonString(new User(1,"Burakhan Aksoy",
                        "burak@burak.com","Male",
                        "12321"))))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$.id").exists())
                .andExpect(jsonPath("$.name").value("Burakhan Aksoy"))
                 .andExpect(jsonPath("$.email").value("burak@burak.com"))
                 .andExpect(jsonPath("$.gender").value("Male"))
                 .andExpect(jsonPath("$.phoneNumber").value("12321"));

        verify(userService,atLeastOnce()).addUser(user1);
//        verifyNoMoreInteractions(userService);
    }
    public static String asJsonString(final Object obj) {
        try {
            final ObjectMapper mapper = new ObjectMapper();
            final String jsonContent = mapper.writeValueAsString(obj);
            System.out.println(jsonContent);
            return jsonContent;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

Any help is appreciated

Best,

  • Does your `User` class have a properly implemented `equals` method? In your `verify` call, you're expecting it to receiving the argument `user1`, but your controller's handler method is actually calling `addUser` with a new `User` object deseralized from the JSON content. Unless there's a proper `equals` method to compare them, they'll never be equal (iirc). – Sotirios Delimanolis Mar 02 '21 at 05:43
  • Thank you for your answer. I don't have equals method. How can I implement it? – Burakhan Aksoy Mar 02 '21 at 05:48
  • https://stackoverflow.com/questions/8180430/how-to-override-equals-method-in-java – Sotirios Delimanolis Mar 02 '21 at 05:48
  • As a follow up question, even though I override the equals method and make sure they are indeed the same, how can I use it in the test case? I mean I will still be using verify(userService,atLeastOnce()).addUser(user); . so how does overriding equals method and making sure user1 and user are equal help this test case pass? – Burakhan Aksoy Mar 02 '21 at 05:51
  • The `equals` method is used for you behind the scenes. Mockito will compare what you `verify()` with what is actually passed to `addUser` in your `@PostMapping` method. – Sotirios Delimanolis Mar 02 '21 at 05:53

1 Answers1

1

Try to overload default hashCode() and equals() method in your User class.

In your scenario, it's considering two different object and comparing based on default Object's method which checks for address.

Edit: As part of this single test case, you just need to override equals() method. But failing to override hashCode(), you might face issues in future when you use any Hash related collections like HashTable, HashMap etc.

Refer this for more details: Why do I need to override the equals and hashCode methods in Java?

Naresh J
  • 1,959
  • 4
  • 24
  • 38