I am using Mockito to test one of my get-mappings in the controller class. Here is my get method
#PostMapping(value = "insert/carbooking")
public ResponseEntity<Void> reservation(#Valid BookingRequest bookRequest) {
return validate(bookRequest, carService::booking);
}
At the top of my class is my Validator
#Autowired
private ReservationValidator reservationValidator;
#InitBinder("bookRequest")
protected void bookRequestBinder(WebDataBinder binder) {
binder.addValidators(reservationValidator);
}
Here is the Mockito test method. The result should have returned bad request since the sin is in wrong format.
#Test
public void reservationTest2() throws Exception {
mockMvc.perform(MockMvcRequestBuilders
.post("insert/carbooking")
.param("license", "data")
.param("SIN", "202007191517")
.accept(MediaType.MULTIPART_FORM_DATA))
.andExpect(status().isBadRequest());
}
But the test fail
java.lang.AssertionError: Status expected:<400> but was:<200>
Expected :400
Actual :200
Is there any way for the Mockito to receive the "reservationValidator" ?
I got everything fixed thanks to #chrylis-onstrike- . For the source code, I just need to remove #Before addholders set up method and replace it with the annotation #Autowired on Mockmvc mockmvc and #MockBean on service class which put the client class on the application context. It's such a shame I could not give tick mark to his answer since he didn't post the answer to my question rather commenting on it.
Related
Here a test code sample with Spring Boot Test:
#SpringBootTest
#AutoConfigureMockMvc
class SendMoneyControllerSpringBootSpyBeanTest {
#Autowired
private MockMvc mockMvc;
#SpyBean
private SendMoneyUseCase sendMoneyUseCase;
#Test
void testSendMoneyWithMock() {
...
}
#Test
void testSendMoneyWithSpy() {
...
}
}
Now suppose the two test methods like in the snippet above. One is using the spy version of the spring bean, whereas the other is using a mock version of the spring bean. How can I mix up both and distinguish them in my test methods ?
For example, can I do :
#SpringBootTest
#AutoConfigureMockMvc
class SendMoneyControllerSpringBootSpyBeanTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private SendMoneyUseCase sendMoneyUseCaseMocked;
#SpyBean
private SendMoneyUseCase sendMoneyUseCaseSpied;
}
I know that in spring bean container, there is only one of sendMoneyUseCaseMocked or sendMoneyUseCaseSpied because they are the same java type. I can use a qualifier, to refer them by name. But in that case, how do I write my mock condition in the corresponding test method (write either mock condition on the mocked bean or the spied condition on the spied bean in the concerned test method).
EDIT : Another approach is to remove the line of code #MockBean, like this, the spied method is working. Consequently, I need then to programmatically code #MockBean in the mocked test method with Spring boot API, but how to do ?.
Thx.
I see two solutions.
Either use two test classes. One with the #MockBean object and one with the #SpyBean object. Both can inherit from the same abstract parent-class.
Let Spring Boot inject #MockBean and then replace it manually in the testSendMoneyWithSpy() test with the #SpyBean object using reflection.
But, why do you want to use the spy-object at all?
If you make unit-tests, you should mock the service and only test the controller here. And test the SendMoneyUseCase class in the SendMondeyUseCaseTest test.
(btw. strange name SendMondeyUseCase why not SendMoneyService or SendMoneyComponent. Or MoneySendService?)
Here the solution, first the solution code (my real test case, the one in first topic is more like a study code) :
#Slf4j
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class DocumentsApiTest extends ProductAPITestRoot {
#Autowired
private TestRestTemplate restTemplate;
#SpyBean
private AlfrescoService alfrescoServiceSpy;
#MockBean
private CloseableHttpClient closeableHttpClient;
#Test
public void testUploadDocument_SUCCESS() {
HttpHeaders headers = createOKHttpHeaders();
DocumentWithFile documentWithFile = createDocumentWithFile();
doReturn(StringUtils.EMPTY).when(alfrescoServiceSpy).sendDocument(anyString(), any(InputStream.class), eq(documentWithFile.getNomFichier()));
HttpEntity<DocumentWithFile> request = new HttpEntity<>(documentWithFile, headers);
ResponseEntity<Long> response = restTemplate.exchange(
"/documents/upload", HttpMethod.POST, request, Long.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isEqualTo(1L);
}
#Test
public void testUploadDocument_500_INTERNAL_SERVER_ERROR() {
HttpHeaders headers = createOKHttpHeaders();
DocumentWithFile documentWithFile = createDocumentWithFile();
doThrow(new AlfrescoServiceException("Alfresco has failed !")).when(alfrescoServiceSpy).sendDocument(anyString(), any(InputStream.class), eq(documentWithFile.getNomFichier()));
HttpEntity<DocumentWithFile> request = new HttpEntity<>(documentWithFile, headers);
ResponseEntity<String> response = restTemplate.exchange(
"/documents/upload", HttpMethod.POST, request, String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
assertThat(response.getBody()).contains("Internal Server Error");
}
#Test
public void testUploadDocument_502_BAD_GATEWAY() throws IOException {
HttpHeaders headers = createOKHttpHeaders();
DocumentWithFile documentWithFile = createDocumentWithFile();
CloseableHttpResponse alfrescoResponse = mock(CloseableHttpResponse.class);
when(closeableHttpClient.execute(any(HttpPost.class))).thenReturn(alfrescoResponse);
when(alfrescoResponse.getStatusLine()).thenReturn(new BasicStatusLine(HttpVersion.HTTP_1_1, HttpStatus.BAD_GATEWAY.value(), "FINE!"));
HttpEntity<DocumentWithFile> request = new HttpEntity<>(documentWithFile, headers);
ResponseEntity<String> response = restTemplate.exchange(
"/documents/upload", HttpMethod.POST, request, String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_GATEWAY);
assertThat(response.getBody()).contains("Erreur Alfresco");
}
private static HttpHeaders createOKHttpHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + OK_TOKEN);
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
private static DocumentWithFile createDocumentWithFile() {
String fileAsString = RandomStringUtils.randomAlphabetic((int) 1e6);
byte[] fileAsStringBase64 = Base64.getEncoder().encode(fileAsString.getBytes());
DocumentWithFile documentWithFile = new DocumentWithFile();
String nomFichierExpected = "nomFichier.pdf";
documentWithFile
.id(8L)
.idCatalogue("idCatalogue")
.nom("nom")
.reference("reference")
.version("version")
.type(TypeDocument.IPID)
.fichier(new String(fileAsStringBase64))
.nomFichier(nomFichierExpected);
return documentWithFile;
}
}
If you look carrefully, the right way to mixup both : a mock and a spy of the same bean.
You can use #MockBean....and you use the spy version of this #MockBean with when(...).thenCallRealMethod(). But the REAL drawback of this is that if the #MockBean bean contains #Value field injection, thus they are NOT initialized. Meaning that #MockBean annotation set #Value fields of the mocked bean to null.
So I went for solution 2) because I need injection of #Value fileds.
Instead of #MockBean use #SpyBean of the concerned spring bean. Like this, you've got now the real bean. The question is how do I use it like a #MockBean. So to use a #SpyBean like a #MockBean, you needs to force the returned value of your #SpyBean bean like this for example :
doReturn(StringUtils.EMPTY).when(alfrescoServiceSpy).sendDocument(anyString(), any(InputStream.class), eq(documentWithFile.getNomFichier()));
As you can see, although alfrescoServiceSpy call the real code (not a mock), then you still can change its default behavior (calling the real code) with the mocked behavior like the snipped code above as example.
So test methods that need the mock version of #SpyBean declare an instruction of mocked behavior to do. And test methods that needs real code they don't do anything and #Value will be injected into the bean annotated #SpyBean the right way.
I'am personnaly satisfied of this code version.
Thanks you very much all.
I am trying to unit test a Spring MVC controller method, but my unit test keeps failing.
I have a Spring MVC controller method:
// MyController.java
#RequestMapping(value = "/end-point", method = RequestMethod.GET)
public ModelAndView getData(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
ModelAndView mv = new ModelAndView();
DataManager dataManager = DataManager.getInstance();
DataUser currentUser = (DataUser) request.getSession().getAttribute("currentUser");
List<DataItem> dataList = dataManager.getDataForUser(currentUser.getId());
mv.addObject("dataList", dataList);
mv.setViewName("home-page");
return mv;
}
I am trying to test this controller method with JUnit. I have very little experience unit testing and am trying to learn. Seems like this is near-impossible or does not make sense to do without a mocking library, and the project I'm working on already has Mockito as a dependency so I'm trying to use that. My test class is below:
//MyControllerTest.java
public class MyControllerTest {
#InjectMocks
private MyController myController;
#Mock
HttpServletRequest request;
#Mock
HttpServletResponse response;
#Mock
ModelAndView mockModelAndView;
#Mock
DataManager mockDataManager;
#Mock
DataUser mockDataUser;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void getDataTest() throws Exception {
//I guess I have to somehow mock mockDataUser here even more than #Mock???
Mockito.when(request.getSession().getAttribute("currentUser")).thenReturn(mockVendorUser); // <-- this is where the null pointer exception is coming from
Mockito.when(myController.getData(request, response)).thenReturn(mockModelAndView);
ModelAndView testing = profileControllerWH.getMySkus(request, response);
assertEquals(1, 1);
}
}
When I run my test, it fails and I get a java.lang.NullPointerException exception in the console, specifying the line above with the null pointer exception comment.
I have tried looking up how to mock classes with Mockito, and I keep seeing the #Mock annotation, which I already have as #Mock DataUser (as well as other classes I am using in the controller method that I guess I need to mock).
How do I get this to work? It seems like I have to create a whole new DataUser object with fake data, but then that seems to defeat the purpose of the mocking library. What would the point of using Mockito be if I have to create my own objects with fake data? Or, I might be misunderstanding this because of lack of experience.
Remember that by default, unstubbed methods return default value of return type when called (0 for numbers, null for Objects).
You haven't stubbed request.getSession() so it returns null.
You need to:
provide a mock for session
stub request.getSession() to return this mock
stub session.getAttribute("currentUser")
On top of that:
While calling the controller method in your test certainly has value of testing the method body, but you will test more functionality (like request and response serialization) if you re-implement your test as a #WebMvcTest
i've implemented a Interceptors for RestTemplate (RestTemplateInterceptor.class):
#Autowired
private RestConfigurations configurations;
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.additionalInterceptors((ClientHttpRequestInterceptor) (httpRequest, bytes, clientHttpRequestExecution) -> {
HttpHeaders headers = httpRequest.getHeaders();
headers.add("X-API-KEY", configurations.getApiKey());
return clientHttpRequestExecution.execute(httpRequest,bytes);
}).build();
}
Now I want to test it. :)
One try looks like that:
#ExtendWith(SpringExtension.class)
#RestClientTest({RestTemplateInterceptor.class, RestConfigurations.class})
#AutoConfigureMockMvc
#SpringBootTest
class MyTestClass {
public static final String URL = "http://testMe.com/";
#Autowired
MockRestServiceServer mockServer;
#Autowired
RestTemplateBuilder restTemplateBuilder;
#Test
#DisplayName("Should include header X-API-KEY")
void headerShouldContainsXApiKey() throws Exception {
mockServer.expect(requestTo(URL)).andRespond(withSuccess("Hello world", TEXT_PLAIN));
String body = restTemplateBuilder.build().exchange(URL//
, GET, //
null, //
String.class).getBody();
assertThat(body).isEqualTo("Hello world");
mockServer.expect(header("X-API-KEY", ""));
}
But i failed with:
java.lang.AssertionError: No further requests expected: HTTP GET http://test.hornbach.de/
0 request(s) executed.
Anyone a hint? Thanks
If you check the Javadoc, you'll see that when you call additionalInterceptors, you're not modifying the existing builder instance but instead getting a new builder with a slightly different configuration. When you then call restTemplateBuilder.build() in your test case, you're building a template that has the unmodified configuration.
The ideal way to test something like this is to do so directly on the interceptor instance itself, but if you're wanting to perform a functional test by calling through to a mock server, you should inject the completed RestTemplate instance into your test case.
I am having trouble writing a JUnit test. The issue is that there is a final method getMessage() in the following block of code:
if(these_conditions_are_true) {
String message = messageResources.getMessage(CONSTANT_STRING, null, LocaleContextHolder.getLocale());
modelView.getModelMap().addAttribute(INLINE_MSG, message);
return modelView;
}
messsageResources is a ReloadableResourceBundleMessageSource which extends AbstractMessageSource, which is an abstract class.
getMessage() is a final method in AbstractMessageSource.
Here is the condensed version of my Test class:
#RunWith(PowerMockRunner.class)
#PrepareForTest({AbstractMessageSource.class, ClassBeingTested.class})
public class ClassBeingTestedTest {
#InjectMocks
ClassBeingTested classBeingTested;
#Mock
ReloadableResourceBundleMessageSource messageResources; //being used by a different test in this class, including in case it matters
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
// Build the mock
this.mockMvc = MockMvcBuilders.standaloneSetup(classBeingTested).build();
}
#Test
public void Method_Being_Tested_Test() {
AbstractMessageSource amsMock = PowerMockito.mock(AbstractMessageSource.class);
Mockito.when(amsMock.getMessage(any(), any(), any())).thenReturn("test01");
ModelAndView mv = classBeingTested.methodBeingTested(itemVO);
ModelAndView mv_Expected = createExpectedMV(); //not really how i'm doing it, but for the sake of simplicity i'll omit it
assertEquals(mv_Expected, mv);
}
}
I'm using PowerMockito since I learned that Mockito can't stub final methods.
The error I get is
No message found under code 'CONSTANT_STRING' for locale 'EN_US'.
It appears that my when...thenReturn isn't actually being used as getMessage() is being called. I've tried adding CALLS_REAL_METHOD to the line where I create the Mock, but that didn't seem to work.
Does anyone know how to stub this out? Am I right to mock AbstractMessageSource, rather than ReloadableResourceBundleMessageSource? Any help would be greatly appreciated!
UPDATE:
I'm still looking for help with this. I'm still running into the issue of not being able to intercept getMessage() actually being called...
Try this
Mockito.when(
amsMock.getMessage(
eq(CONSTANT_STRING),
eq(null),
any(Locale.class))).thenReturn("test01");
Edit: This strategy is terrible,
because you will need to mock the getMessage method for every property that is accessed during your unit test.
What you appear to want is the ability to set a property value for use during your unit tests.
If this is the case,
include a property configuration for your unit tests.
For spring,
you can create a unit test configuration that references the test properties and load it with the SpringRunner.
I'm testing a class with nested (Autowired) dependencies. The class implements businesslogic for making alterations in the backend. Specifically the test should assert that when a certain backend call returns an error:
No more backend calls are made
The response object returns an error
I dont know how the do the latter.
My class looks something like this:
public class Handler {
#Autowired
private DaoImpl dao;
#Autowired
private SpecificUtil util1;
#Autowired
private GeneralUtil util2;
#Autowired
private Helper helper;
public Response doSomethingClever(Request request) {
// calls to dao
// logic with help of util and helper classes
}
}
The testclass:
public class HandlerTest {
#Spy
private DaoImpl dao;
#Mock
private SpecificUtil util1;
#Mock
private GeneralUtil util2;
#Mock
private Helper helper;
#InjectMocks
Handler handler;
#Test
public void testDoSomethingClever() {
// set up dao to fail
QueryResult error = getErrorResult();
org.mockito.Mockito.when(dao.queryBackEnd(any(SpecificQuery.class))).thenReturn(error);
// perform query
Request request = getTestRequest();
Response errorResponse = handler.doSomethingClever(request);
// verify that either:
// Response has errors - fails
// because helper classes are mocks, have not set the error
assertNotNull(response.getErrorMessage());
// the method setErrors of Response was called once - fails
//because the setError was called earlier!
Response spyResponse = Mockito.spy(errorResponse);
verify(spyResponse, times(1)).setError(anyString);
//verify no other calls are made except the queryBackEnd call - this part works
org.mockito.Mockito.verify(dao).queryBackEnd(any(SpecificQuery.class));
org.mockito.Mockito.verifyNoMoreInteractions(dao);
}
}
The Response object is created in the Handler class. If i check the returned response, no interactions will be recorded by Mockito, because the interactions have taken place prior to calling Mockito.spy.
I have tried making it an integration test, by using #Spy instead of #Mock. The idea is to instantiate all the nested dependencies except for the dao and get a proper Response to test for errors. But this does not work because some of the #Autowired helper and utility classes also have #Autowired dependencies and these nested dependencies are not instantiated during testing.
Is there any way to inject #Spy objects into other #Spy objects with Mockito?
Or is there some other solution in this case? Sould i write my own mockobjects?
A unit test should test the code of a specific unit only (here the Handler class). This includes interacting with the dependencies.
From what you wrote in your question and comments your handle method looks something along these lines:
public class Handler {
#Autowired
private DaoImpl dao;
#Autowired
private Util util;
public Response doSomethingClever(Request request) {
SpecificQuery specificQuery = new SpecificQuery();
specificQuery.setSomeData(request.getSomeData());
IntermidiateResponse intermidiateResponse = dao.queryBackEnd(specificQuery);
Response response = util.processIntermidiateResult(intermidiateResult);
return response;
}
}
There are a few interactions to unit test here:
Given a Request instance assert that DaoImpl::queryBackEnd method is called with a SpecificQuery instance that has someData property set to someData property from the Request object
Given a mocked IntermidiateResponse being returned from the DaoImpl::queryBackEnd method assert that this result is passed on to the Util::processIntermidiateResult method
Given a mocked Response being returned from the Util::processIntermidiateResult method assert that this is exactly what gets returned from the handle method
This way you have 100% coverage on the Handler::handle method. If you have some more calls in the response processing pipeline you test them all accordingly.
Hope this answers your question. Good luck
There are two options, one is to test the Handler in isolation, mocking everything else. See the answer by jannis. The other option is to control/mock the in- and output of the Handler, and treat the Handler and all its utility classes as a black box.
I have opted for this last option because it means that i can refactor the utility-classes, and the Handler class itself, and that tests will succeed as long as i don't change what the Handler does. It does mean that i have departed somewhat from doing unit testing and that i'm really doing more of an integration test.
For this i mock the Dao class, so that i can have it return an error at the desired point and so that i can assert that no further calls are made after the error.
It is the only class i mock, so i need to inject it into the Handler. This is possible with Springs ReflectionTestUtils (I did'nt know about this yesterday)
The testcode then becomes shorter:
public class HandlerTest {
#Autowired
private Handler handler;
#Test
public void testDoSomethingClever() {
// set up dao to fail
Dao mockDao = org.mockito.Mockito.mock(DaoImpl.class);
QueryResult error = getErrorResult();
org.mockito.Mockito.when(dao.queryBackEnd(any (SpecificQuery.class))).thenReturn(error);
// inject the dao
ReflectionTestUtils.setField(handler, "dao", mockDao);
// perform query
Request request = getTestRequest();
Response errorResponse = handler.doSomethingClever(request);
// verify that Response has errors
assertNotNull(response.getErrorMessage());
//verify no other calls are made except the queryBackEnd call
org.mockito.Mockito.verify(dao).queryBackEnd(any(SpecificQuery.class));
org.mockito.Mockito.verifyNoMoreInteractions(dao);
}
}