I want build a unit test for a Servlet using JUnit and JMockit.
I have an ImageServlet which takes image IDs (String) as request parameters and if ID is null the servlet throws a HTTP status code 404 (not found)
for this scenario I have the test:
Unit Test:
#RunWith(JMockit.class)
public class ImageServletTest {
#Tested
private ImageServlet servlet;
#Injectable
HttpServletRequest mockHttpServletRequest;
#Injectable
HttpServletResponse mockHttpServletResponse;
#Injectable
PrintWriter printWriter;
#Injectable
ServletOutputStream servletOutputStream;
#Before
public void setUp() throws Exception {
servlet = new ImageServlet();
initMocks(null);
}
private void initMocks(final String imgId) throws Exception {
new NonStrictExpectations() {{
mockHttpServletRequest.getParameter("id");
result = imgId;
mockHttpServletResponse.getWriter();
result = printWriter;
mockHttpServletResponse.getOutputStream();
result = servletOutputStream;
}};
}
#Test
public void testImageNotFound() throws Exception {
servlet.doGet(mockHttpServletRequest, mockHttpServletResponse);
org.junit.Assert.assertTrue(mockHttpServletResponse.getStatus() == HttpServletResponse.SC_NOT_FOUND);
}
}
the problem is that my Assertion fails as mockHttpServletResponse.getStatus() always returns 0, is there a way to get the resulting Status code of the servlet using JMockit?
I'm not familiar with all the latest JMockit injection stuff, so I used JMockits support for "fakes".
#RunWith(JMockit.class)
public class ImageServletTest3 {
#Test
public void testImageNotFound() throws Exception {
ImageServlet servlet = new ImageServlet();
servlet.doGet(
new MockUp<HttpServletRequest>() {
#Mock
public String getParameter(String id){
return null;
}
}.getMockInstance(),
new MockUp<HttpServletResponse>() {
#Mock
public void sendError(int num){
Assert.assertThat(num, IsEqual.equalTo(404));
}
}.getMockInstance()
);
}
}
Related
I'm creating the base for my unit testing project( Spring boot rest controller) and I'm having a problem passing #InjectMocks value because it's only evaluated in #Before and therefore a nullpointer is thrown when i try to access it outside
Some tips to get around the problem please?
Thank you very much
Ps : Any other advices on best practices or something i did wrong for unit testing regarding my current base class test will be appreciated as well
Class to test (rest controller)
#RestController
#RequestMapping("/management")
#Api(description = "Users count connections", produces = "application/json", tags = {"ConnectionManagement API"})
public class ConnectionManagementControllerImpl implements ConnectionManagementController {
#Autowired
private ConnectionManagementBusinessService connectionManagementBusinessService;
#Override
#PostMapping(value = "/countConnectionsByInterval" , consumes = MediaType.TEXT_PLAIN_VALUE , produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
#ApiOperation(value = "count all users connections by interval")
public ResponseEntity<List<ConnectionsCountDto>> countConnectionsByInterval(#RequestBody String format) {
List<ConnectionsCountDto> connectionManagement = connectionManagementBusinessService.countConnectionsByInterval(format);
return new ResponseEntity<List<ConnectionsCountDto>>(connectionManagement, HttpStatus.OK);
}
Abstract base test
public abstract class AbstractBaseTest<C> {
public MockMvc mockMvc;
private Class<C> clazz;
private Object inject;
protected abstract String getURL();
protected final void setTestClass(final Class<C> classToSet, final Object injectToSet) {
clazz = Preconditions.checkNotNull(classToSet);
inject = Preconditions.checkNotNull(injectToSet);
}
#Before
public void init() throws Exception {
MockitoAnnotations.initMocks(clazz);
mockMvc = MockMvcBuilders.standaloneSetup(inject).build();
}
protected MockHttpServletResponse getResponse(MediaType produces) throws Exception {
MockHttpServletResponse response = mockMvc.perform(
get(getURL()).
accept(produces)).
andReturn().
getResponse();
return response;
}
protected MockHttpServletResponse postResponse(String content , MediaType consumes , MediaType produces) throws Exception {
MockHttpServletResponse response = mockMvc.perform(
post(getURL()).
content(content).
contentType(consumes).
accept(produces)).
andReturn().
getResponse();
return response;
}
}
Test class
#RunWith(MockitoJUnitRunner.class)
public class ConnectionManagementControllerImplTest extends AbstractBaseTest<ConnectionManagementControllerImpl>{
#Mock
private ConnectionManagementBusinessService connectionManagementBusinessServiceMocked;
#InjectMocks
private ConnectionManagementControllerImpl connectionManagementControllerMocked;
public ConnectionManagementControllerImplTest() {
super();
setTestClass(ConnectionManagementControllerImpl.class , connectionManagementControllerMocked); // null pointer there
}
#Test
public void countConnectionsByInterval() throws Exception {
// given
given(connectionManagementBusinessServiceMocked.countConnectionsByInterval(Mockito.anyString()))
.willReturn(new ArrayList<ConnectionsCountDto>());
// when
MockHttpServletResponse response = postResponse("day" , MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON_UTF8);
// then
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
}
#Override
protected String getURL() {
return "/management/countConnectionsByInterval";
}
This works as intended. However, you can setup mocks manually and inject them inside ConnectionManagementControllerImplTest constructor (before calling setTestClass(...)):
public ConnectionManagementControllerImplTest() {
super();
connectionManagementBusinessServiceMocked = Mockito.mock(ConnectionManagementBusinessService.class);
connectionManagementControllerMocked = new ConnectionManagementControllerImpl();
connectionManagementControllerMocked.setConnectionManagementBusinessService(connectionManagementBusinessServiceMocked);
setTestClass(ConnectionManagementControllerImpl.class, connectionManagementControllerMocked);
}
Do not forget to remove #Mock and #InjectMocks annotations. Btw you can even remove #RunWith(MockitoJUnitRunner.class) in that case.
UPDATE: Both the constructor of test class and "init" method annotated with #Before are executed for each test. The difference is that Mockito annotations are processed between constructor and #Before method invocations.
So you can slightly change your code in order to achieve a positive result:
Create "init" method (annotated with #Before) inside ConnectionManagementControllerImplTest and move setTestClass() into it from the constructor (in that particular case you can also remove the whole constructor because it would contain only super() invocation).
Add super.init() after setTestClass() line (otherwise "init" method in the parent class will be ignored by JUnit).
(Optional) you could also remove #Before annotation from the "init" method in the parent class if your tests are written in the same manner.
The example of code refactored in that way:
public abstract class AbstractBaseTest<C> {
public MockMvc mockMvc;
private Class<C> clazz;
private Object inject;
protected abstract String getURL();
protected final void setTestClass(final Class<C> classToSet, final Object injectToSet) {
clazz = Preconditions.checkNotNull(classToSet);
inject = Preconditions.checkNotNull(injectToSet);
}
#Before //this annotation can be removed
public void init() throws Exception {
MockitoAnnotations.initMocks(clazz); //this line also can be removed because MockitoJUnitRunner does it for you
mockMvc = MockMvcBuilders.standaloneSetup(inject).build();
}
protected MockHttpServletResponse getResponse(MediaType produces) throws Exception {
MockHttpServletResponse response = mockMvc.perform(
get(getURL()).
accept(produces)).
andReturn().
getResponse();
return response;
}
protected MockHttpServletResponse postResponse(String content , MediaType consumes , MediaType produces) throws Exception {
MockHttpServletResponse response = mockMvc.perform(
post(getURL()).
content(content).
contentType(consumes).
accept(produces)).
andReturn().
getResponse();
return response;
}
}
#RunWith(MockitoJUnitRunner.class)
public class ConnectionManagementControllerImplTest extends AbstractBaseTest<ConnectionManagementControllerImpl> {
#Mock
private ConnectionManagementBusinessService connectionManagementBusinessServiceMocked;
#InjectMocks
private ConnectionManagementControllerImpl connectionManagementControllerMocked;
//constructor can be removed
public ConnectionManagementControllerImplTest() {
super();
}
#Before
public void init() throws Exception {
setTestClass(ConnectionManagementControllerImpl.class, connectionManagementControllerMocked);
super.init();
}
#Test
public void countConnectionsByInterval() throws Exception {
// given
given(connectionManagementBusinessServiceMocked.countConnectionsByInterval(Mockito.anyString()))
.willReturn(new ArrayList<ConnectionsCountDto>());
// when
MockHttpServletResponse response = postResponse("day", MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON_UTF8);
// then
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
}
#Override
protected String getURL() {
return "/management/countConnectionsByInterval";
}
}
P.S. I'd prefer the former approach, but if you don't want to have a setter for ConnectionManagementBusinessService, you can choose the latter. I've tested both of them and the result was the same.
Help
#Override
public String postRequestinTransactionService(String data) {
RequestTransaction request = new RequestTransaction(data.getClass().getName(), data);
HttpEntity<RequestTransaction> entity = new HttpEntity<RequestTransaction>(request);
ResponseEntity<String> response = restTemplate.exchange(this.urlTransactions, HttpMethod.POST, entity,
String.class);
return response.getBody();
}
Here is barebone test class for you. You can write test case and if you have specific problem then ask question.
#RunWith(MockitoJUnitRunner.class)
public class YourClassNameTest{
#InjectMocks
YourClassUnderTest myClass;
private String data;
#Before
public void setUp() throws Exception {
//prepare you data here
// any other mock action you can set here
}
#Test
public void testPostRequestinTransactionService() throws Exception {
//Write you test here
String result=myClass.postRequestinTransactionService(data);
assertThat("result should be blablabla", result, is("blablabla");
}
I'm trying to unit test the code of my Presenter. As you can see below in the code I'm making a Retrofit request and if the response is successful I call a method from the View.
Code of my Presenter I want to test :
#Override
public void onLoadChatrooms(String accountId, String pageNum) {
getChatroomsService.getChatrooms(apiToken, createRequestBodyForGetChatroomsRequest(accountId, pageNum))
.enqueue(new Callback<GetChatroomsServiceResponse>() {
#Override
public void onResponse(Call<GetChatroomsServiceResponse> call, Response<GetChatroomsServiceResponse> response) {
if (response.isSuccessful()) {
view.showData(Arrays.asList(response.body().getChatRoomsArray()));
}
}
#Override
public void onFailure(Call<GetChatroomsServiceResponse> call, Throwable t) {
}
});
}
And here is the test I wrote :
#Mock
private ChatMVP.View view;
#Mock
private GetChatroomsService getChatroomsService;
#Mock
private RequestBody requestBody;
#Mock
private Call<GetChatroomsServiceResponse> call;
#Captor
private ArgumentCaptor<Callback<GetChatroomsServiceResponse>> callback;
#Mock
private List<GetChatroomsResponseNestedItem> chatroomsResponseNestedItems;
private String accountId = "14";
private String apiToken = "someToken";
private ChatPresenter chatPresenter;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
chatPresenter = new ChatPresenter(view, getChatroomsService, apiToken);
}
#Test
public void onLoadChatrooms() throws Exception {
when(getChatroomsService.getChatrooms(apiToken, requestBody))
.thenReturn(call);
chatPresenter.onLoadChatrooms(accountId, "0");
verify(call).enqueue(callback.capture());
callback.getValue().onResponse(call, getResponse());
verify(view).showData(chatroomsResponseNestedItems);
}
The problem is that I'm getting a NPE for line :
chatPresenter.onLoadChatrooms(accountId, "0");
The exact error message is :
java.lang.NullPointerException
at my.package.main.fragments.chat.ChatPresenter.onLoadChatrooms(ChatPresenter.java:40)
at my.package.main.fragments.chat.ChatPresenterTest.onLoadChatrooms(ChatPresenterTest.java:70)
where line 40 for ChatPresenter is : .enqueue(new Callback<GetChatroomsServiceResponse>() {
Anyone can help with that ? I tried checking if Presenter is null and that's not the problem.
EDIT :
ChatPresenter's constructor :
class ChatPresenter implements ChatMVP.Presenter {
private ChatMVP.View view;
private GetChatroomsService getChatroomsService;
private String apiToken;
#Inject
ChatPresenter(ChatMVP.View view, GetChatroomsService getChatroomsService, #Named("Api-Token") String apiToken) {
this.view = view;
this.getChatroomsService = getChatroomsService;
this.apiToken = apiToken;
}
and GetChatroomsService :
interface GetChatroomsService {
#POST("getchatrooms")
Call<GetChatroomsServiceResponse> getChatrooms(#Query("api_token") String apiToken, #Body RequestBody requestBody);
}
The problem here is that the mocked method getChatrooms() in getChatroomsService returns a null. The most likely reason for this is that the parameters given in your production code do not match the parameters in your mock configuration.
I for myself use the any*() matcher when configuring the mocks and verify the parameters passed in by the production code explicitly which saves me from non descriptive NPEs like this.
#Test
public void onLoadChatrooms() throws Exception {
when(getChatroomsService.getChatrooms(anyString(), any(RequestBody.class)))
.thenReturn(call);
chatPresenter.onLoadChatrooms(accountId, "0");
verify(call).enqueue(callback.capture());
callback.getValue().onResponse(call, getResponse());
verify(getChatroomsService).getChatrooms(apiToken,requestBody);
verify(view).showData(chatroomsResponseNestedItems);
}
I have following controller code for which I have to write JUnit test case.
public class EquipmentController {
private Map<String, Equipment> equiList = new HashMap <String,Equipment>();
#RequestMapping("/rest/equipment/{Number}")
public Equipment getEquipment(#PathVariable String Number){
if(!equiList.containsKey(Number)){
lNumber = DEFAULT;
}
return equiList.get(Number);
}
}
I'm writing the JUnit test case for the same as below:
import static org.springframework.test.web.ModelAndViewAssert.*;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({/* include live config here
e.g. "file:web/WEB-INF/application-context.xml",
"file:web/WEB-INF/dispatcher-servlet.xml" */})
public class EquipmentControllerTest {
#Inject
private ApplicationContext applicationContext;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private HandlerAdapter handlerAdapter;
private EquipmentController controller;
#Before
public void setUp() {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
handlerAdapter = applicationContext.getBean(HandlerAdapter.class);
// Get the controller from the context here
controller = new EquipmentController();
}
#Test
public void testgetEquipment() throws Exception {
request.getUriString()("lNumber");
final Equipment equip = handlerAdapter.handle(request, response,
controller);
assertViewName(equip, "view");
}
}
But am not sure if this test class is correct or not as I am new to JUnit.
Can anyone please suggest how to do this.
Create a mock of your controller and use MockMvc to test your methods:
import static org.springframework.test.web.ModelAndViewAssert.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({/* include live config here
e.g. "file:web/WEB-INF/application-context.xml",
"file:web/WEB-INF/dispatcher-servlet.xml" */})
public class EquipmentControllerTest {
private MockMvc mockMvc;
private EquipmentController controller;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.standaloneSetup(equipmentController).build()
}
#Test
public void testgetEquipment() throws Exception {
this.mockMvc.perform(get("/rest/equipment/{Number}", 3))
.andExpect(status().isOk())
}
}
where "3" represents value of your path variable.
I ran into a problem the other day where a #Valid annotation was accidentally removed from a controller class. Unfortunately, it didn't break any of our tests. None of our unit tests actually exercise the Spring AnnotationMethodHandlerAdapter pathway. We just test our controller classes directly.
How can I write a unit or integration test that will correctly fail if my #MVC annotations are wrong? Is there a way I can ask Spring to find and exercise the relevant controller with a MockHttpServlet or something?
I write integration tests for this kind of thing. Say you have a bean with validation annotations:
public class MyForm {
#NotNull
private Long myNumber;
...
}
and a controller that handles the submission
#Controller
#RequestMapping("/simple-form")
public class MyController {
private final static String FORM_VIEW = null;
#RequestMapping(method = RequestMethod.POST)
public String processFormSubmission(#Valid MyForm myForm,
BindingResult result) {
if (result.hasErrors()) {
return FORM_VIEW;
}
// process the form
return "success-view";
}
}
and you want to test that the #Valid and #NotNull annotations are wired correctly:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"file:web/WEB-INF/application-context.xml",
"file:web/WEB-INF/dispatcher-servlet.xml"})
public class MyControllerIntegrationTest {
#Inject
private ApplicationContext applicationContext;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private HandlerAdapter handlerAdapter;
#Before
public void setUp() throws Exception {
this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse();
this.handlerAdapter = applicationContext.getBean(HandlerAdapter.class);
}
ModelAndView handle(HttpServletRequest request, HttpServletResponse response)
throws Exception {
final HandlerMapping handlerMapping = applicationContext.getBean(HandlerMapping.class);
final HandlerExecutionChain handler = handlerMapping.getHandler(request);
assertNotNull("No handler found for request, check you request mapping", handler);
final Object controller = handler.getHandler();
// if you want to override any injected attributes do it here
final HandlerInterceptor[] interceptors =
handlerMapping.getHandler(request).getInterceptors();
for (HandlerInterceptor interceptor : interceptors) {
final boolean carryOn = interceptor.preHandle(request, response, controller);
if (!carryOn) {
return null;
}
}
final ModelAndView mav = handlerAdapter.handle(request, response, controller);
return mav;
}
#Test
public void testProcessFormSubmission() throws Exception {
request.setMethod("POST");
request.setRequestURI("/simple-form");
request.setParameter("myNumber", "");
final ModelAndView mav = handle(request, response);
// test we're returned back to the form
assertViewName(mav, "simple-form");
// make assertions on the errors
final BindingResult errors = assertAndReturnModelAttributeOfType(mav,
"org.springframework.validation.BindingResult.myForm",
BindingResult.class);
assertEquals(1, errors.getErrorCount());
assertEquals("", errors.getFieldValue("myNumber"));
}
See my blog post on integration testing Spring's MVC annotations
Sure. There's no reason why your test can't instantiate its own DispatcherServlet, inject it with the various items which it would have in a container (e.g. ServletContext), including the location of the context definition file.
Spring comes with a variety of servlet-related MockXYZ classes for this purpose, including MockServletContext, MockHttpServletRequest and MockHttpServletResponse. They're not really "mock" objects in the usual sense, they're more like dumb stubs, but they do the job.
The servlet's test context would have the usual MVC-related beans, plus your beans to test. Once the servlet is initialized, create the mock requests and responses, and feed them into the servet's service() method. If request gets routed correctly, you can check the results as written to the mock response.
In upcoming spring 3.2 (SNAPSHOT available) or with spring-test-mvc (https://github.com/SpringSource/spring-test-mvc) you can do it like this:
first we emulate Validation as we do not want to test the validator, just want to know if validation is called.
public class LocalValidatorFactoryBeanMock extends LocalValidatorFactoryBean
{
private boolean fakeErrors;
public void fakeErrors ( )
{
this.fakeErrors = true;
}
#Override
public boolean supports ( Class<?> clazz )
{
return true;
}
#Override
public void validate ( Object target, Errors errors, Object... validationHints )
{
if (fakeErrors)
{
errors.reject("error");
}
}
}
this is our test class:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration
public class RegisterControllerTest
{
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Autowired
#InjectMocks
private RegisterController registerController;
#Autowired
private LocalValidatorFactoryBeanMock validator;
#Before
public void setup ( )
{
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
// if you want to inject mocks into your controller
MockitoAnnotations.initMocks(this);
}
#Test
public void testPostValidationError ( ) throws Exception
{
validator.fakeErrors();
MockHttpServletRequestBuilder post = post("/info/register");
post.param("name", "Bob");
ResultActions result = getMockMvc().perform(post);
// no redirect as we have errors
result.andExpect(view().name("info/register"));
}
#Configuration
#Import(DispatcherServletConfig.class)
static class Config extends WebMvcConfigurerAdapter
{
#Override
public Validator getValidator ( )
{
return new LocalValidatorFactoryBeanMock();
}
#Bean
RegisterController registerController ( )
{
return new RegisterController();
}
}
}