Spring Boot Restcontroller as inner class in unit test (webmvctest) - java

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 = []

Related

Error in Junit testcases for a contoller file

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.

Spring boot WebMvcTest cases keep getting terminated and won't run

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;

How to test mvc controller method with http response argument?

Can you help me? I need to test this controller method. But don't know what to do with httpservletresponse object.
#Controller
public class HomeController {
#PostMapping("/signout")
public String signOut(HttpServletResponse response){
response.addCookie(new Cookie("auth-token", null));
return "redirect:http://localhost:3000";
}
}
Thank you)
Spring MVC Test provides an effective way to test controllers by performing requests and generating responses through the actual DispatcherServlet.
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import org.junit.Assert;
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.boot.test.context.SpringBootTest;
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.ResultMatcher;
#RunWith(SpringRunner.class)
#WebMvcTest(controllers=HomeController.class)
public class HomeControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testSignOut() throws Exception {
mockMvc.perform(post("/signout"))
.andDo(print())
.andExpect(new ResultMatcher() {
#Override
public void match(MvcResult result) throws Exception {
Assert.assertEquals("http://localhost:3000",result.getResponse().getRedirectedUrl());
}
});
}
}
In case of Spring MVC without spring boot, use standalone MockMvc support
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration // or #ContextConfiguration
public class HomeControllerTest{
#Autowired
private HomeController homeController;
private MockMvc mockMvc;
#Before
public void setup() {
// Setup Spring test in standalone mode
this.mockMvc =
MockMvcBuilders.standaloneSetup(homeController).build();
}

Test the Spring Boot Rest API Post Method

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.

Spring testing controller does not work

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

Categories