Unit testing spring mvc controller with mockito

17,786

Put your line

verify(serviceMock, times(1)).update(first);

after

mockMvc.perform(
        put("/api/hashService/" + hashToCheck)
                .contentType(TestUtil.APPLICATION_JSON_UTF8)
                .content(
                        TestUtil.convertObjectToJsonBytesWithSettersOnly(first))
                .accept(TestUtil.APPLICATION_JSON_UTF8));

but before mockMvc assertions, to verify that your mock is used.

General issues with your code:

  • With annotation @RunWith(MockitoJUnitRunner.class) initializing Mockito manually is not necessary.

    @MockitoJUnitRunner

    Initializes mocks annotated with Mock, so that explicit usage of MockitoAnnotations.initMocks(Object) is not necessary.

  • Unit testing

    i'm looking for a way to test the 3 layers structure.

    When three layers are involved in the test, the test is an integration test.

  • In unit testing it is better to have many small method for testing different behaviour. So one method should test HTTP Response, second should verify if service is invoked.

Share:
17,786
oak
Author by

oak

Updated on June 04, 2022

Comments

  • oak
    oak almost 2 years

    i'm using mockito and junit and run unit test against spring mvc my flow of code is:

    Service Layer -> Model Layer -> Controller Layer

    i was successfully testing the Controller against the model layer with the code:

    @RunWith(MockitoJUnitRunner.class)
    public class HashLinkerControllerTest {
    
    private static final Logger LOGGER = Logger
            .getLogger(HashLinkerControllerTest.class);
    
    
    
    @Mock
    private HashLinkerModel modelMock;
    
    @InjectMocks
    private HashLinkerController controller;
    
    private MockMvc mockMvc;
    
    @Before
    public void setup() {
    
        MockitoAnnotations.initMocks(this);
        this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
    
    }
    
      @Test
    public void update() throws Exception {
        int date = 123;
        String hashToCheck = "asdfasdfasdfasdfas";
        HashLinker first = this.createHashLinker(hashToCheck, "some name",
                "some des", "some data", date);
    
        when(modelMock.update(first)).then(returnsFirstArg());
        mockMvc.perform(
                put("/api/hashService/" + hashToCheck)
                        .contentType(TestUtil.APPLICATION_JSON_UTF8)
                        .content(
                                TestUtil.convertObjectToJsonBytesWithSettersOnly(first))
                        .accept(TestUtil.APPLICATION_JSON_UTF8))
                .andExpect(status().isOk())
                .andExpect(
                        content().contentType(TestUtil.APPLICATION_JSON_UTF8))
                .andExpect(jsonPath("$.hash", is(hashToCheck)))
                .andExpect(jsonPath("$.description", is("some des")))
                .andExpect(jsonPath("$.name", is("some name")));
    
        verify(modelMock, times(1)).update(first);
        verifyNoMoreInteractions(modelMock);
    }
    

    but now i'm looking for a way to test the 3 layers structure. i.e testing the service against the controller . i came up with :

    @RunWith(MockitoJUnitRunner.class)
    public class HashLinkerControllerServiceLayerTest {
    
    private static final Logger LOGGER = Logger
            .getLogger(HashLinkerControllerServiceLayerTest.class);
    
    @Mock
    private HashLinkerService serviceMock;
    
    @InjectMocks
    // @Mock
    private HashLinkerModel modelMock;
    
    private HashLinkerController controller;
    
    private MockMvc mockMvc;
    
    @Before
    public void setup() {
    
        MockitoAnnotations.initMocks(this);
        controller = new HashLinkerController(modelMock);
        this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
    
    }
    

    and doing the test with

    @Test
    public void update() throws Exception {
        int date = 123;
        String hashToCheck = "asdfasdfasdfasdfas";
        HashLinker first = this.createHashLinker(hashToCheck, "some name",
                "some des", "some data", date);
    
        when(serviceMock.update(first)).then(returnsFirstArg());
        mockMvc.perform(
                put("/api/hashService/" + hashToCheck)
                        .contentType(TestUtil.APPLICATION_JSON_UTF8)
                        .content(
                                TestUtil.convertObjectToJsonBytesWithSettersOnly(first))
                        .accept(TestUtil.APPLICATION_JSON_UTF8))
                .andExpect(status().isOk())
                .andExpect(
                        content().contentType(TestUtil.APPLICATION_JSON_UTF8))
                .andExpect(jsonPath("$.hash", is(hashToCheck)))
                .andExpect(jsonPath("$.description", is("some des")))
                .andExpect(jsonPath("$.name", is("some name")));
    
        verify(serviceMock, times(1)).update(first);
        verifyNoMoreInteractions(serviceMock);
    }
    

    the test is the same as above expect i have changed modelMock to be serviceMock. for some reason this test fails . i get 404. any ideas?

    version:

    <spring.core.version>3.2.6.RELEASE</spring.core.version> <spring.test.version>3.2.6.RELEASE</spring.test.version> <mockito.version>1.9.5</mockito.version> <junit.version>4.8.2</junit.version>

    EDIT:

    @Component
    public class HashLinkerModel {
    
    private static final Logger LOGGER = Logger.getLogger(HashLinkerModel.class);
    
    HashLinkerService service;
    
    /** The unicode service. */
    UnicodeService unicodeService;
    
    @Autowired
    public HashLinkerModel(HashLinkerService service, UnicodeService unicodeService){
        this.service = service;
        this.unicodeService = unicodeService;
    
    }
    
    
    
    
         /**
     * check if hash exists before creation
     * @param toHash
     * @return
     */
    private HashLinker checkAndcreate(HashLinker toHash){
    
    
        String hash = this.createHash(toHash.getData());
        toHash.setHash(hash);
    
        LOGGER.info("add " + hash + ". this is the hash of string " + toHash.getData());
    
        /* if this hash already in system. return null. otherwise create */
        HashLinker exists =  service.findByHash(toHash.getHash());
    
        if (null != exists) 
            return exists;
    //** we use service here ** 
        return service.create(toHash);
    }
    
    public HashLinker create(HashLinker entity) {
        // TODO Auto-generated method stub
    
        RestPreconditions.checkRequestElementNotNull(entity, LOGGER);
        RestPreconditions.checkRequestElementNotNull(entity.getData(), LOGGER);
    
        String uniName = getUniHelper().unicode(entity.getName());
        String uniDesc = getUniHelper().unicode(entity.getDescription());
    
        HashLinker toHash = new HashLinker();
        toHash.setData(entity.getData());
    
        toHash.setType(entity.getType());
    
        String baseURL = entity.getBaseURL();
    
        if (null != baseURL) 
                baseURL  = (baseURL.length() > HashLinker.getIntMaxBaseURL()) ? baseURL.substring(0, HashLinker.getIntMaxBaseURL()) : baseURL;
    
        toHash.setBaseURL(baseURL);
        toHash.setStatus(0);
        // update lecker's macros results
        toHash.setName(uniName);
        toHash.setDescription(uniDesc);
    
    
        return checkAndcreate(toHash);
    
    }
    
  • oak
    oak over 10 years
    thanks for the tips. tried the put verify statement after perform. service doesn't get called. infact i get error of 404. which means something with the controller doesn't work properly, isn't ? maybe my 3rd layer structure for how to init the test are not allowed with spring ?
  • MariuszS
    MariuszS over 10 years
    @oak We need more data, Show us HashLinkerModel and HashLinkerService