Unit testing spring mvc controller with mockito

2019-07-09 02:46发布

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);

}

1条回答
Viruses.
2楼-- · 2019-07-09 03:25

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.

查看更多
登录 后发表回答