Unit testing spring mvc controller with mockito
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.
oak
Updated on June 04, 2022Comments
-
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 beserviceMock
. 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 over 10 yearsthanks 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 over 10 years@oak We need more data, Show us
HashLinkerModel
andHashLinkerService