I am new to spring boot and Junit. I am writing a small Junit testcase for my controller.
My controller file looks like below:
#RequestMapping(path = "/project-files")
public class MyController {
private RequestModel setRequestModel(String params) {
RequestModel requestModel = new RequestModel();
requestModel.setRequestData(params);
return requestModel;
}
#PostMapping("/deleteUploadedFile")
#ResponseBody
public Map<String, Object> deleteUploadedFile(#RequestParam(value = "requestData", required = false) String params) {
RequestModel requestModel = setRequestModel(params);
return service.deleteUploadedFile(requestModel);
}
}
Based on the reference from here I tried to write a junit tescases for my controller.
My Junit Testcases looks like below:
package com.test.project.web;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.test.project.beans.RequestModel;
import com.test.project.service.serviceImpl;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
public class MyControllerTest<mapFromJson> {
#InjectMocks
private serviceImpl serviceImpl;
#InjectMocks
dataRepoImpl repoImpl;
#InjectMocks
MyController myController;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(new MyController()).build();
}
protected <T> T mapFromJson(String json, Class<T> clazz) throws
JsonParseException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper(); return
objectMapper.readValue(json, clazz);
}
#Test
public void deletefile() throws Exception {
String uri = "/deleteUploadedFile";
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get(uri).accept(MediaType.APPLICATION_JSON_VALUE))
.andReturn();
int status = mvcResult.getResponse().getStatus();
assertEquals(200, status);
String content = mvcResult.getResponse().getContentAsString();
MyController[] productlist = super.mapFromJson(content, MyController[].class);
assertTrue(productlist.length > 0);
}
}
I am getting errors
java.lang.IllegalStateException: Failed to load ApplicationContext
Is there any other better way to write the Junit testcases for controller?
#Test public void deletefile() throws Exception {
MvcResult mvcResult = mockMvc.perform(post("/project-files/deleteUploadedFile") //Getting error in post.
.param("params", "some_value_here"))
.andExpect(status().isOk()); //Getting error in status
String content = mvcResult.getResponse().getContentAsString();
productlist = super.mapFromJson(content, MrmController[].class); //Getting error in productlist & mapFromJson
assertTrue(productlist.length > 0);
}
You can use the .andExpect() method to validate the status of the request instead of using assertEquals()
You can use something in the lines of this to test your endpoint call:
mockMvc.perform(post("/project-files/deleteUploadedFile")
.param("params", "some_value_here"))
.andExpect(status().isOk());
Some strange things I find inside of your controller is that you use #PostMapping for deleting you can use #DeleteMapping instead see spring docs here
Also, you should use more descriptive names for parameter names (and in general)
String params
don't really ring a bell as to what this variable is representing.
If the issue persists make sure to post the entire stacktrace as suggested.
*Keep in mind when you're writing tests in general you are mimicking the behaviour of the real call, so don't add useless checks or mocks
#InjectMocks
private serviceImpl serviceImpl;
#InjectMocks
dataRepoImpl repoImpl;
I don't see the above in your controller so I am not sure why are they part of the test.
Related
When I try using the below code to test the controllers no errors happen, it just says terminated with no logged messages or anything.
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import ....controllers.UserController;
import ....data.response.UserResponse;
import ....models.user.User;
#RunWith(SpringRunner.class)
#WebMvcTest(UserController.class)
public class UserWebMvc {
#Autowired
private MockMvc mvc;
#Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray() throws Exception {
User alex = new User();
List<UserResponse> allUsers = Arrays.asList(new UserResponse(alex.getId(), alex.getInfo()));
RequestBuilder request = MockMvcRequestBuilders.get("/hello");
MvcResult result = mvc.perform(request).andReturn();
assertEquals(result.getResponse().getContentAsString(), "hello");
// userService.createUser(
// new UserRequest(alex.getInfo().getName(), alex.getInfo().getEmail(), alex.getInfo().getPassword()));
//
}
}
However my test to check that junit is working runs fine, so I'm thinking its something to do with SpringRunner or WebMvc
#SpringBootTest
class BackendApplicationTests {
#Test
void contextLoads() {
assertTrue(false);
}
}
Isn't assertTrue(false) always going to be false? You are basically checking whether false equals true, which will always fail.
Also, from the Spring documentation regarding #WebMvcTest:
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. #Controller, #ControllerAdvice, #JsonComponent, Converter/GenericConverter, Filter, WebMvcConfigurer and HandlerMethodArgumentResolver beans but not #Component, #Service or #Repository beans).
Using this annotation, it will not scan any beans from the service layer. So if you have a UserService which is injected into the UserController, it will not be found by Spring.
When I run tests like this, I would do something like:
class UserControllerTest {
#InjectMocks
private UserController userController;
private MockMvc mockMvc;
#BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
mockMvc =
MockMvcBuilders.standaloneSetup(userController).build();
}
#Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray()
throws Exception {
User alex = new User();
List<UserResponse> allUsers = Arrays.asList(new
UserResponse(alex.getId(), alex.getInfo()));
RequestBuilder request = MockMvcRequestBuilders.get("/hello");
MvcResult result = mvc.perform(request).andReturn();
assertEquals(result.getResponse().getContentAsString(),
"hello");
}
}
I was using the wrong import. Changing to this fixed it
import static org.junit.Assert.assertTrue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
I have a method coming from another class which is the super class
for the method I am testing. I wish to mock it such that when I reach that method,
I just want to return a mocked data.
But currently, it keeps going into the method instead of skipping it and just assigning the mock data.
Could I please get some help on why my mocking is not working?
Please note that the parent class is actually coming from another dependency which we do not maintain and it's accessibility is protected.
Thus can't refactor the method being mocked and not looking to refactor the code. Just want to mock it.
Looking to achieve this either via mockito or powermock or combined if it comes to that. Thanks. ## Heading ##
This is the class and method being tested.
#Service
public class OfferService extends ParentClass {
// many other methods
public Object get() {
// getHttpEntity() comes from ParentClass
HttpEntity<Object> httpEntity = getHttpEntity("www.example.com"); // looking to mock this getHttpEntity method. I do not want to enter it.
// will not reach here. Failed at above line cos didn't mock getHttpEntity.
ResponseEntity<OfferResponses> responseEntity = restTemplate.exchange(endPoint, HttpMethod.GET, httpEntity, OfferResponses.class);
return responseEntity.getBody();
}
}
My Test which currently fails cos it keeps entering the getHttpEntity method.
package com.pack;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.powermock.api.mockito.PowerMockito.doReturn;
import static org.powermock.api.mockito.PowerMockito.spy;
import static org.powermock.api.mockito.PowerMockito.whenNew;
#RunWith(PowerMockRunner.class)
public class OfferServiceTest {
#InjectMocks
OfferService offerService;
#Mock
RestTemplate restTemplate;
#Mock
HttpEntity<Object> entity;
#Before
public void setup() {
offerService = new OfferService();
MockitoAnnotations.initMocks(this);
}
// failing test
#Test
public void getTest() throws Exception {
OfferResponses offerResponses = new OfferResponses();
ResponseEntity<OfferResponses> responseEntity = new ResponseEntity<>(offerResponses, HttpStatus.OK);
PowerMockito.when(
restTemplate.exchange(anyString(), Matchers.eq(HttpMethod.GET)
, Matchers.anyObject(),
Matchers.<Class<OfferResponses>>anyObject()))
.thenReturn(responseEntity);
OfferService spy = spy(offerService);
doReturn(entity).when(spy, "getHttpEntity", Matchers.anyString());
Object result = offerService.get();
// assertions
}
}
So, since you want to spy your OfferService, you must invoke your get method on this spy.
With PowerMockito:
#RunWith(PowerMockRunner.class)
public class OfferServiceTest {
#Spy
#InjectMocks
private OfferService offerService = new OfferService();
#Mock
private RestTemplate restTemplate;
#Mock
private HttpEntity<Object> entity;
#Test
public void test() {
PowerMockito.when(offerService, "getHttpEntity", "www.example.com")
.thenReturn(entity);
PowerMockito.when(restTemplate.exchange(anyString(), any(), any(), any()))
.thenReturn(something);
assertEquals(someObject, offerService.get());
}
}
Or it can be done without PowerMockito using ReflectionTestUtils:
#ExtendWith(MockitoExtension.class)
public class OfferServiceTest {
#Spy
#InjectMocks
private OfferService offerService;
#Mock
private RestTemplate restTemplate;
#Mock
private HttpEntity<Object> entity;
#Test
public void test() {
when(ReflectionTestUtils.invokeMethod(offerService, "getHttpEntity", "www.example.com"))
.thenReturn(entity);
when(restTemplate.exchange(anyString(), any(), any(), any()))
.thenReturn(something);
assertEquals(someObject, offerService.get());
}
}
Is it somehow possible to declare a RestController in a test context, preferably as a inner class of a spring boot test? I do need it for a specific test setup. I already did try following simple example as a POC:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
#ExtendWith(SpringExtension.class)
#WebMvcTest(ExampleTest.TestController.class)
#AutoConfigureMockMvc
#AutoConfigureWebClient
public class ExampleTest {
#Autowired
private MockMvc mockMvc;
#Test
public void exampleTest() throws Exception {
ResultActions resultActions = this.mockMvc
.perform(get("/test"));
resultActions
.andDo(print());
}
#RestController
public static class TestController {
#GetMapping("/test")
public String test() {
return "hello";
}
}
}
Testing the endpoint via MockMvc delivers 404 though. Am i missing something?
The problem is, TestController is not loaded to the application context. It could be solved by adding
#ContextConfiguration(classes= ExampleTest.TestController.class)
The test will look like:
#ExtendWith(SpringExtension.class)
#WebMvcTest(ExampleTest.TestController.class)
#ContextConfiguration(classes= ExampleTest.TestController.class)
#AutoConfigureMockMvc
#AutoConfigureWebClient
public class ExampleTest {
#Autowired
private MockMvc mockMvc;
#Test
public void exampleTest() throws Exception {
ResultActions resultActions = this.mockMvc
.perform(get("/test"));
resultActions
.andDo(print());
}
#RestController
public static class TestController {
#GetMapping("/test")
public String test() {
return "hello";
}
}
}
And the output:
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"text/plain;charset=UTF-8", Content-Length:"5"]
Content type = text/plain;charset=UTF-8
Body = hello
Forwarded URL = null
Redirected URL = null
Cookies = []
I completely new to Junit and I have to write Junit Test case for my Rest Controller but I am not getting from where should I start. Any help would be really appreciated.
This is My Rest Controller class.
#RestController
public class RecognitionController {
private FrameDecoder frameDecoder;
private TagEncoder tagEncoder;
private RecognitionService recognitionService;
#Autowired
public RecognitionController(FrameDecoder frameDecoder, TagEncoder tagEncoder,
RecognitionService recognitionService) {
this.frameDecoder = frameDecoder;
this.tagEncoder = tagEncoder;
this.recognitionService = recognitionService;
}
/**
*
* #param take the input as Json Frame and map the output at api/detection Url.
* #return List of Json tag in the Http response.
*/
#RequestMapping(value = "/api/detection", method = RequestMethod.POST)
public List<JsonTag> analyseframe(#RequestBody JsonFrame frame) {
SimpleFrame simpleFrame = frameDecoder.decodeFrame(frame);
List<OrientedTag> orientedTags = recognitionService.analyseFrame(simpleFrame);
return tagEncoder.encodeTag(orientedTags);
}
}
For testing Rest Controller you need:
JUnit
Mockito
Spring Test
JsonPath
Controller:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
#RestController
#RequestMapping(value = "/Entity")
public class EntityRestController {
private EntityService service;
#RequestMapping(value = "/entity/all", method = RequestMethod.GET)
public List<Entity> findAll() {
List<Entity> models = service.findAll();
return createEntities(models);
}
private List<EntityDTO> createDTOs(List<Entity> models) {
List<EntityDTO> dtos = new ArrayList<>();
for (Entitymodel: models) {
dtos.add(createDTO(model));
}
return dtos;
}
private EntityDTO createDTO(Entity model) {
EntityDTO dto = new EntityDTO();
dto.setId(model.getId());
dto.setDescription(model.getDescription());
dto.setTitle(model.getTitle());
return dto;
}
}
Test example:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Arrays;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestContext.class, WebAppContext.class})
#WebAppConfiguration
public class EntityRestControllerTest {
private MockMvc mockMvc;
#Autowired
private EntityService entityServiceMock;
//Add WebApplicationContext field here.
//The setUp() method is omitted.
#Test
public void findAllEntitiesTest() throws Exception {
Entity first = new Entity();
first.setId(1L);
first.setDescription("Lorem ipsum")
first.setTitle("Foo");
Entity second = new Entity();
second.setId(2L);
second.setDescription("Lorem ipsum")
second.setTitle("Bar");
when(entityServiceMock.findAll()).thenReturn(Arrays.asList(first, second));
mockMvc.perform(get("/entity/all"))
.andExpect(status().isOk())
.andExpect(content().contentType(TestUtil.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$", hasSize(2)))
.andExpect(jsonPath("$[0].id", is(1)))
.andExpect(jsonPath("$[0].description", is("Lorem ipsum")))
.andExpect(jsonPath("$[0].title", is("Foo")))
.andExpect(jsonPath("$[1].id", is(2)))
.andExpect(jsonPath("$[1].description", is("Lorem ipsum")))
.andExpect(jsonPath("$[1].title", is("Bar")));
verify(entityServiceMock, times(1)).findAll();
verifyNoMoreInteractions(entityServiceMock);
}
}
Please follow the full tutorial for more details.
___EDIT_1___
I didn't understand from where "thenReturn" Method came
static method Mockito.when() has the following signature:
public static <T> OngoingStubbing<T> when(T methodCall)
When you mocking some service and putting it inside when as parameter - it returns object which IS OngoingStubbing<T>. All classes which implement OngoingStubbing<T> have thenReturn(T value) method and it's called.
I have following class :
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import server.testing.WebTest;
public class CampaignControllerTest extends WebTest {
#Autowired
CampaignController campaignController;
private MockMvc mockMvc;
#Before
public void mySetup() throws Exception {
this.mockMvc = MockMvcBuilders.standaloneSetup(campaignController).build();
}
#Test
public void testStatus() throws Exception{
mockMvc.perform(get("/01/status")).andExpect((status().isOk()));
}
}
And I get following error : Failed tests: testStatus(cz.coffeeexperts.feedback.server.web.controller.CampaignControllerTest): Status expected:<200> but was:<404>
404 is (usually) thrown, if you type wrong address.
This is my CampaingController :
#Controller
#RequestMapping(value = "/api")
public class CampaignController {
#RequestMapping(value = "/01/status", method = RequestMethod.GET)
#ResponseBody
public ServerStatusJSON getStatus() {
return new ServerStatusJSON(activeCampaignService.getActiveCampaign().getIdCampaign());
}
}
This address I try to test works fine when I deploy this application on tomcat. There should not be problem inside that method, because in that case it returns 500 internal error, not 404.
Am I doing something wrong?
Try mockMvc.perform(get("/api/01/status")).andExpect((status().isOk()));