I am trying to create a controller test in springboot that tests whether the controller method adds a new line into my database (I have configured a h2 in memory database). I keep getting errors. I want to pass the data into the controller in json form like this:
{"type":"epidemics",
"questionIndex":21,
"choiceNum":4,
"question":"second question",
"choiceA": "no1",
"choiceB":"yes2",
"choiceC":"no3",
"choiceD":"yes4",
"correct":"no1",
"hint":"second answer"
}
but I keep getting errors. My code is below:
controller
#PostMapping("/adminadd")
public QuizDTO addQuestion(#RequestBody QuizDTO quizDTO) {
return quizRepo.addQuestion(quizDTO);
}
jdbc repo
private static final String INSERT_QUESTION = "INSERT INTO Quiz(type,questionIndex,choiceNum,question,choiceA,choiceB,choiceC,choiceD,correct,hint) values(?,?,?,?,?,?,?,?,?,?)";
#Override
public QuizDTO addQuestion(QuizDTO quizDTO) {
jdbcTemplate.update(INSERT_QUESTION, quizDTO.getType(), quizDTO.getquestionIndex(), quizDTO.getChoiceNum(), quizDTO.getQuestion(), quizDTO.getChoiceA(),
quizDTO.getChoiceB(), quizDTO.getChoiceC(), quizDTO.getChoiceD(), quizDTO.getCorrect(), quizDTO.getHint());
return quizDTO;
}
Test
#RunWith(SpringRunner.class)
#WebMvcTest(QuizController.class)
//#AutoConfigureMockMvc
public class QuizControllerTest {
#MockBean
private QuizRepository quizRepository;
#Autowired
private MockMvc mockMvc;
#Test
void shouldAddQuestion() throws Exception {
QuizDTO quizDTO = new QuizDTO(104, "epidemics", 21, 4, "Test Question?", "A", "B", "C", "D", "hint");
mockMvc.perform(post("/adminadd").contentType(MediaType.APPLICATION_JSON_VALUE)
.param("ID", "104")
.param("Type", "Epidemics")
.param("questionIndex", "21")
.param("choiceNum", "4")
.param("question", "Test Q")
.param("choiceA", "A")
.param("choiceB", "B")
.param("choiceC", "C")
.param("choiceD", "D")
.param("correct", "B")
.param("Hint", "hint"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("Test Q")));;
}
}
error:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /adminadd
Parameters = {ID=[104], Type=[Epidemics], questionIndex=[21], choiceNum=[4], question=[Test Q], choiceA=[A], choiceB=[B], choiceC=[C], choiceD=[D], correct=[B], Hint=[hint]}
Headers = [Content-Type:"application/json;charset=UTF-8"]
Body = null
Session Attrs = {org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN=org.springframework.security.web.csrf.DefaultCsrfToken#22899683}
Handler:
Type = null
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 403
Error message = Forbidden
Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Status expected:<200> but was:<403>
Related
i have a problem testing an endpoint which use #ModelAttribute I don't know very well how to test with this annotation and the test response is java.lang.AssertionError: Content type not set , here is the controller method:
#PostMapping
public ResponseEntity<?> createTestimonials(#ModelAttribute(name = "testimonialsCreationDto") #Valid TestimonialsCreationDto testimonialsCreationDto) {
try {
return ResponseEntity.status(HttpStatus.CREATED).body(iTestimonials.createTestimonials(testimonialsCreationDto));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.CONFLICT).body(e.getMessage());
}
}
Here is the test:
#Test
void createTestimonials() throws Exception {
//Given
String name = "Testimonio 159";
String contentTestimonial = name + " content!";
TestimonialsCreationDto testimonialsCreationDto = new TestimonialsCreationDto();
testimonialsCreationDto.setName(name);
testimonialsCreationDto.setContent(contentTestimonial);
//When
mockMvc.perform(post("/testimonials")
.flashAttr("testimonialsCreationDto", testimonialsCreationDto)
.contentType(MediaType.MULTIPART_FORM_DATA)
.content(objectMapper.writeValueAsString(testimonialsCreationDto))
.characterEncoding("UTF-8"))
//Then
.andExpect(status().isCreated())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.name", is(name)))
.andExpect(jsonPath("$.content", is(contentTestimonial)));
verify(testimonialsService).createTestimonials(any());
}
MockHttpServletRequest:
HTTP Method = POST
Request URI = /testimonials
Parameters = {}
Headers = [Content-Type:"multipart/form-data;charset=UTF-8", Content-Length:"74"]
Body = {"name":"Testimonio 159","image":null,"content":"Testimonio 159 content!"}
Session Attrs = {}
MockHttpServletResponse:
Status = 200 ---> IDK why response with 200 code
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError: Content type not set
You have to add path value to PostMapping :
#PostMapping(path = "/testimonials", produces = MediaType.MULTIPART_FORM_DATA)
I'm trying to test my microservice endpoint Get Payees using MockMvc to perform the get request.
I've already set the behavior with mockito.when(), so whatever ID used in the request, it should return my response.
I have this test:
#ControllerTest
#AutoConfigureMockMvc
public class PayeesApiControllerGetPayeesTest {
private static final String ACCOUNTS_URI = "/payees";
#Autowired
private MockMvc mvc;
#Autowired
private GetPayeesModule module;
#Test
public void testGetPayees() throws Exception {
when(module.execute(
anyString(),
anyInt(),
anyInt(),
anyString(),
anyString(),
anyString(),
anyString(),
anyString(),
anyString()))
.thenReturn(
ResponseEntity.ok()
.header(X_V_HEADER, X_V_VERSION)
.header(X_FAPI_INTERACTION_ID_HEADER, UUID.randomUUID().toString())
.body(
new ResponsePayee()
.data(
new PayeeData()
.addPayeesItem(
new PayeeDetails()
.id(35L)
.type(PayeeType.DOMESTIC)
.fullName("Robert Doe")
.description("My brother")
.bsb("010-147")
.accountNumber("123456789")))));
mvc.perform(
get(ACCOUNTS_URI)
.param("userId", "1")
.accept(MediaType.APPLICATION_JSON)
.header(X_V_HEADER, X_V_VERSION)
.with(jwtWithAccountsBasicReadPermissionAndBankClientRole()))
.andExpect(status().isOk())
.andExpect(content().string("DOMESTIC"));
}
And this is my Controller:
#Controller
#Validated
#RequiredArgsConstructor
public class PayeesApiController implements PayeesApiInterface {
private final GetPayeesModule getPayeesModule;
private final CreatePayeesModule createPayeesModule;
#Override
public ResponseEntity<ResponsePayee> getPayees(
String userId,
Integer page,
Integer pageSize,
String xRequestId,
String xCorrelationContext,
String xCorrelationId,
String xFapiAuthDate,
String xFapiCustomerIpAddress,
String xCdsClientHeaders) {
return getPayeesModule.execute(
userId,
page,
pageSize,
xRequestId,
xCorrelationContext,
xCorrelationId,
xFapiAuthDate,
xFapiCustomerIpAddress,
xCdsClientHeaders);
}
#Controller annotation:
/**
* Meta-annotation (annotation of annotations) to group Spring's annotations for testing web slice.
*/
#WebMvcTest()
#ActiveProfiles("test")
#Import(ControllerTestConfiguration.class)
#Retention(RetentionPolicy.RUNTIME)
public #interface ControllerTest {}
This is what my console shows:
2021-01-26 18:56:58.010 INFO 17428 --- [ main] c.m.s.o.HttpServletLogService : REQUEST m:[GET], p:[/payees], q:[null], h:[{empty}], b:[{empty}]
MockHttpServletRequest:
HTTP Method = GET
Request URI = /payees
Parameters = {userId=[1]}
Headers = [Accept:"application/json", x-v:"1"]
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = com.marlo.payees.api.PayeesApiController
Method = com.marlo.payees.api.PayeesApiController#getPayees(String, Integer, Integer, String, String, String, String, String, String)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [X-Correlation-Id:"f7d29ced-1ddc-4788-93fb-ba6655da412d", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
------EDIT--------
Adding the ApiInterface example on how I use RequestMapping
#RequestMapping(
value = "/payees",
produces = {"application/json"},
method = RequestMethod.GET)
default ResponseEntity<ResponsePayee> getPayees
For some reason, I always get empty body even with 200 status code.
If you guys could help me... i would appreciate it.
Thanks in advance.
If I understood well, your
private GetPayeesModule module;
in
public class PayeesApiControllerGetPayeesTest {
should be annotated with
#MockBean
and not with
#Autowired
I'm learning Java Spring and trying to write a test of a controller method. That is, I want to perform a controller method by its path and check if the returning content contains some sequence of symbols. For example, my test is invoking a content on the page by address "user/1" and expecting that content will contain "root".
Unfortunately, performing "user/1" returns null content, although it's possible to input this route in a browser and get a page of this user without any problems.
Here is my controller method that I want to test
#GetMapping("/{id}")
public String view(#PathVariable(value = "id") Integer id, Model model) throws EntityNotFoundException {
try {
User item = users.get(id);
model.addAttribute("item", item);
model.addAttribute("posts", posts.getLatestByUser(item, 10));
model.addAttribute("comments", comments.getLatestByUser(item, 10));
return "user/view";
} catch (IllegalArgumentException ex) {
throw new EntityNotFoundException("Cannot find a user with id = " + id);
}
}
here is my class for testing
#SpringBootTest(classes = {
Main.class,
TestDataConfig.class,
TestWebConfig.class
})
#ActiveProfiles("test")
#AutoConfigureMockMvc
#Sql(value = {"/create-tables.sql", "/fill-tables.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
public class UserCtrlTest {
#Autowired
private UserCtrl controller;
#Autowired
private MockMvc mockMvc;
#Test
#WithAnonymousUser
public void shouldDisplayUserInfoUponWatchingUserPage() throws Exception {
MvcResult result = this.mockMvc.perform(get("/user/1"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(view().name("user/view"))
.andReturn();
String stringResult = result.getResponse().getContentAsString();
Assert.assertTrue(stringResult.contains("<h4>root</h4>"));
}
}
Here is my view resolver class
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setViewClass(JstlView.class);
bean.setPrefix("/WEB-INF/views/");
bean.setSuffix(".jsp");
return bean;
}
Here is result that the method .andDo(print()) returns in the console
MockHttpServletRequest:
HTTP Method = GET
Request URI = /user/1
Parameters = {}
Headers = []
Body = null
Session Attrs = {}
Handler:
Type = ru.job4j.forum.controller.UserCtrl
Method = ru.job4j.forum.controller.UserCtrl#view(Integer, Model)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = user/view
View = null
Attribute = item
value = ru.job4j.forum.model.User#20
errors = []
Attribute = posts
value = [ru.job4j.forum.model.Post#20, ru.job4j.forum.model.Post#21]
Attribute = comments
value = [ru.job4j.forum.model.Comment#21, ru.job4j.forum.model.Comment#24]
Attribute = user
value = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Language:"en", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
Content type = null
Body =
Forwarded URL = /WEB-INF/views/user/view.jsp
Redirected URL = null
Cookies = []
MockHttpServletRequest:
HTTP Method = GET
Request URI = /user/1
Parameters = {}
Headers = []
Body = null
Session Attrs = {}
Handler:
Type = ru.job4j.forum.controller.UserCtrl
Method = ru.job4j.forum.controller.UserCtrl#view(Integer, Model)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = user/view
View = null
Attribute = item
value = ru.job4j.forum.model.User#20
errors = []
Attribute = posts
value = [ru.job4j.forum.model.Post#20, ru.job4j.forum.model.Post#21]
Attribute = comments
value = [ru.job4j.forum.model.Comment#21, ru.job4j.forum.model.Comment#24]
Attribute = user
value = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Language:"en", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
Content type = null
Body =
Forwarded URL = /WEB-INF/views/user/view.jsp
Redirected URL = null
Cookies = []
Could you help me to understand why the request doesn't return the content of the page? And how to solve this problem? If you need other sources and screenshots to clarify the situation, please, write about that and I will add them.
I'm testing an api endpoint and MockMvc throws org.springframework.web.HttpMediaTypeNotSupportedException Status expected:<201> but was:<415>
Read all SO about similar problems - always solution is in content-type, but not in this case )
What is strange, I cannot see in MockHttpServletResponse's headers application/json.
Controller:
public static final String MEDIA_TYPE_APPLICATION_JSON_UTF8 = "application/json;charset=utf-8";
#PostMapping(value = "/api/Register", consumes = MEDIA_TYPE_APPLICATION_JSON_UTF8, produces = MEDIA_TYPE_APPLICATION_JSON_UTF8)
public ResponseEntity<Map<String, String>> register(#RequestBody Map<String, String> jsonMap) {
...
Map<String, String> responseMap = new HashMap<>();
MediaType MEDIA_TYPE_JSON_UTF8 = new MediaType("application", "json", StandardCharsets.UTF_8);
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MEDIA_TYPE_JSON_UTF8));
headers.setContentType(MEDIA_TYPE_JSON_UTF8);
...
return new ResponseEntity<>(new Gson().toJson(responseMap), headers, HttpStatus.CREATED);
}
Test:
#Test
void Register_phoneNumber_returnOk() throws Exception {
Map<String, String> body = new HashMap<>();
body.put("phone", "1112223344");
Gson gson = new Gson();
String json = gson.toJson(body);
MockHttpServletRequestBuilder request = post("/api/Register");
request.content("{\"phone\":\"1112223344\"}");
request.accept(MEDIA_TYPE_APPLICATION_JSON_UTF8);
request.contentType(MEDIA_TYPE_APPLICATION_JSON_UTF8);
mockMvc.perform(request)
.andDo(print())
.andExpect(status().isCreated());
}
Error:
WARN 6188 --- [main] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=utf-8' not supported]
MockHttpServletRequest:
HTTP Method = POST
Request URI = /api/Register
Parameters = {}
Headers = [Content-Type:"application/json;charset=utf-8", Accept:"application/json;charset=utf-8"]
Body = {"phone":"1112223344"}
Session Attrs = {}
Handler:
Type = ru.controllers.MainController
Method = ru.controllers.MainController#Register(Map)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.web.HttpMediaTypeNotSupportedException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 415
Error message = null
Headers = [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", Accept:"application/octet-stream, text/plain, application/xml, text/xml, application/x-www-form-urlencoded, application/*+xml, multipart/form-data, multipart/mixed, */*"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError: Status expected:<201> but was:<415>
Answer was quite simple.
Service with #MockBean annotation in test class was creating entities and saving them to db, then controller was returning entity id, that did NPE in entity.getId() and, of course, HTTP 415 in rest controller's test method. At the same time while testing in real db, postman did not show an error, because service layer always returned real id in .getId() method.
After i modified service layer code to check entity for null, problem was gone.
I've generated fresh project using Spring Initializer (https://start.spring.io/)
Spring boot - 2.0.0.RC1 which ships spring in version 5.0.3.RELEASE.
Added the following controller:
#RestController
#ResponseBody
#RequestMapping(value = "/customer")
public class CustomerController {
#GetMapping("/")
public Future<ResponseEntity<String>> get1() {
return CompletableFuture.supplyAsync(() -> new ResponseEntity<String ("not found", HttpStatus.NOT_FOUND));
}
}
and having the following dependencies section in build.gradle (problem can be also replicated on maven build)
dependencies {
compile('org.springframework.boot:spring-boot-starter')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework:spring-web')
testCompile('org.springframework:spring-test')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
The following test fails:
#SpringBootTest
#RunWith(SpringRunner.class)
#AutoConfigureMockMvc
public class CustomerControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void test404() throws Exception {
MvcResult asyncResult = mockMvc.perform(MockMvcRequestBuilders.get("/customer/"))
.andExpect(request().asyncStarted())
.andReturn();
mockMvc.perform(asyncDispatch(asyncResult))
.andExpect(status().isNotFound());
}
}
with the following log:
MockHttpServletRequest:
HTTP Method = GET
Request URI = /customer/
Parameters = {}
Headers = {}
Body = null
Session Attrs = {}
Handler:
Type = com.example.wkicior.demo.CustomerController
Method = public java.util.concurrent.Future<org.springframework.http.ResponseEntity<java.lang.String>> com.example.wkicior.demo.CustomerController.get1()
Async:
Async started = true
Async result = <404 Not Found,not found,{}>
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
MockHttpServletRequest:
HTTP Method = GET
Request URI = /customer/
Parameters = {}
Headers = {}
Body = null
Session Attrs = {}
Handler:
Type = com.example.wkicior.demo.CustomerController
Method = public java.util.concurrent.Future<org.springframework.http.ResponseEntity<java.lang.String>> com.example.wkicior.demo.CustomerController.get1()
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {Content-Type=[text/plain;charset=UTF-8], Content-Length=[9]}
Content type = text/plain;charset=UTF-8
Body = not found
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError: Status
Expected :404
Actual :200
It seems that the test sees status code as 200, not 404. The body content is returned properly, though.
What's interesting that downgrading to 5.0.0.RELEASE of spring-test - fixes the problem:
testCompile('org.springframework:spring-test:5.0.0.RELEASE')
Is this a spring-test bug?
It seems like spring bug indeed: https://jira.spring.io/browse/SPR-16430
moving to 5.0.4.BUILD-SNAPSHOT resolves the problem