I'm a new developer. Can someone tell me why did the following unit test fail?
Here is UserController.java
#RequiredArgsConstructor
#RestController
public class UserController {
private final UserService userService;
#PostMapping(value="/v1/user")
public ResponseEntity<ResponseDto> postUser(#RequestBody UserSaveRequestDto userSaveRequestDto) {
Long userId = userService.insertUser(userSaveRequestDto);
return CommonUtil.getResponseEntity(UserResponseDto.builder()
.userId(userId)
.build()
, HttpStatus.OK
, "회원 등록 완료");
}
}
Here is UserControllerTest.java
given(userService.insertUser(userSaveRequestDto)).willReturn(1L); // not working
so ".andExpect(jsonPath("$.data.userId").value("1"))" is fail
Please let me know why given() doesn't work.
#WebMvcTest
public class UserControllerTest {
MockMvc mockMvc;
#MockBean // Mock Bean은 Mock과 달리 Container가 관리하도록 빈을 만듬, 일반적으로 MockMvc와 많이씀
UserService userService;
#Autowired
ObjectMapper objectMapper;
#Autowired
private WebApplicationContext ctx;
UserSaveRequestDto userSaveRequestDto;
#BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(ctx)
.addFilters(new CharacterEncodingFilter("UTF-8", true)) // 한글 깨짐 처리
.build();
userSaveRequestDto = UserSaveRequestDto.builder()
.userName("test")
.userPhoneNumber("01026137832")
.build();
}
#DisplayName("MockMvc를 이용한 postUser slice 테스트")
#Test
public void postUserTest() throws Exception {
// given
given(userService.insertUser(userSaveRequestDto)).willReturn(1L); // Controller가 의존하고 있는 Service객체의 행동을 설정 해준다.
String content = objectMapper.writeValueAsString(userSaveRequestDto); // dto to json
// when
ResultActions resultActions = mockMvc.perform(post("/v1/user")
.contentType(MediaType.APPLICATION_JSON)
.content(content));
// then
resultActions
// ResultActions 객체의 andDo, andExpect, andReturn 메서드 사용
.andDo(result -> {
if (result.getResolvedException() != null) {
result.getResolvedException().printStackTrace();
}
})
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.userId").value("1"))
.andExpect(jsonPath("$.message").value("회원 등록 완료"));
}
}
Related
I need to write unit tests for a Spring Controller class.
The setup is like this:
#RestController
#RequestMapping("/")
public class MyCustomController {
#Autowired
private MagicWriter magicWriter;
#Autowired
private MagicUpdater magicUpdater;
#RequestMapping(path = "/", method = RequestMethod.POST)
public String postMagicMethod(#RequestParam(name = "SomeParam") String param1) {
var magicHandler = new MagicHandler(magicWriter, magicUpdater);
return magicHandler.doSomeMagic();
}
}
From my JUnit test, I need to use #MockBean for magicWriter and magicUpdater class.
So far I could not find anything constructive.
Here is my Unit test
#SpringJUnitConfig
#WebMvcTest(value= MyCustomController.class)
public class MyCustomControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private MagicWriter magicWriter;
#MockBean
private MagicUpdater magicUpdater;
#Autowired
private WebApplicationContext webApplicationContext;
#Configuration
static class Config {
#Bean
MyCustomController dispatchController() {
return new MyCustomController();
}
}
#Test
void basicTest() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
HttpHeaders headers = new HttpHeaders();
// Added some http headers
String uri = "/";
RequestBuilder request = MockMvcRequestBuilders.request(HttpMethod.POST, uri, headers);
MvcResult result = mockMvc.perform(request).andReturn();
assertThat(result.getResponse().getContentAsString()).isEqualTo(expected);
}
}
Convert your #Autowired parameters to be constructor based and not field-based.
#RestController
#RequestMapping("/")
public class MyCustomController {
private MagicWriter magicWriter;
private MagicUpdater magicUpdater;
#Autowired
public MyCustomController(MagicWriter magicWriter, MagicUpdater magicUpdater) {
this.magicWriter = magicWriter;
this.magicUpdater = magicUpdater;
}
// ... rest of your code
}
Then in your test, you just new an instance of this class with your mocks passed in. You're already resigned to using mock beans, so you don't need to whole Spring Context to come along.
// Unit test code example
MyCustomController testObject;
MagicWriter magicWriterMock;
magicUpdater magicUpdaterMock;
#BeforeEach
void setUp() throws Exception {
magicWriterMock = mock(MagicWriter.class);
magicUpdaterMock = mock(MagicUpdater.class);
testObject = new MyCustomController(magicWriterMock, magicUpdaterMock);
}
#GetMapping(value = "/abc")
public ResponseDto<Map<String, FResponseDto>, Void> getFlightDetails(#RequestParam String fIds) {
Map<String, FResponseDto> response = fService
.getDetails(fIds);
if (Objects.isNull(response)) {
return ResponseDto.failure(FConstants.FLIGHT_NOT_FOUND);
}
return ResponseDto.success(FConstants.SUCCESS,response);
}
how to test this other then status i want to test the if and other return
This code may help you.
#WebMvcTest(controllers = YourController.class)
class YourControllerTest {
private MockMvc mockMvc;
#Autowired
private YourController controller;
#MockBean
private FService fService;
#BeforeEach
public void setUp() {
this.mockMvc = MockMvcBuilders.standaloneSetup(controller)
.build();
}
#Test
public void test_getFlightDetails() throws Exception {
Map<String, FResponseDto> data = ......;
Mockito.when(fService.getDetails(anyString())).thenReturn(data);
this.mockMvc.perform(get("/abc"))
.andExpect(status().isOk());
Mockito.when(fService.getDetails(anyString())).thenReturn(null);
this.mockMvc.perform(get("/abc"))
.andExpect(status().is5xxServerError());
}
}
Currently struggling with problem when I get 'mapping error for request' with following controller/test configuration.
Controller:
#Slf4j
#Validated
#RestController
#RequiredArgsConstructor
public class AdtechController {
private final AdtechService adtechService;
#PostMapping(value = "/subscriber/session")
public ResponseEntity<ResponseDto> submitSession(#RequestBody RequestDto requestDto) {
log.trace("execute submitSession with {}", requestDto);
ResponseDtoresponse = adtechService.submitSession(requestDto);
return new ResponseEntity<>(response, HttpStatus.OK);
}
#ExceptionHandler(AdtechServiceException.class)
public ResponseEntity<AdtechErrorResponse> handleAdtechServiceException(AdtechServiceException e) {
return new ResponseEntity<>(new AdtechErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Test:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
#SpringJUnitConfig({AdtechTestConfig.class})
public class AdtechControllerTest {
private static final ObjectMapper OBJECT_MAPPER = JsonUtil.getJackson();
#Autowired
private MockMvc mockMvc;
#Test
public void testSubmitSession() throws Exception {
RequestDto requestDto = new RequestDto ();
requestDto.setKyivstarId("1123134");
requestDto.setMsisdn("123476345242");
requestDto.setPartnerId("112432523");
requestDto.setPartnerName("125798756");
String request = OBJECT_MAPPER.writeValueAsString(requestDto);
System.out.println("REQUEST: " + request);
String response = OBJECT_MAPPER.writeValueAsString(new ResponseDto("123"));
System.out.println("RESPONSE: " + response);
mockMvc.perform(post("/subscriber/session")
.content(MediaType.APPLICATION_JSON_VALUE)
.content(request))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string(containsString(response)));
}
}
Configuration:
#Configuration
public class AdtechTestConfig {
#Bean
public AdtechService adtechTestService() {
return requestDto -> new AdtechResponseDto("123");
}
}
After test execution I get No mapping for POST /subscriber/session
The reason for the struggle is that my code from other modules with the same configuration works fine. Can somebody point out what am I missing ? Thanks in advance!
Apparently you are loading a configuration class to mock beans, this interferes with the other parts of Spring Boot and probably leads to partially loading your application. I suspect only the mocked service is available.
Instead of the test configuration use #MockBean to create a mock for the service and register behaviour on it.
#SpringBootTest
#AutoConfigureMockMvc
public class AdtechControllerTest {
private static final ObjectMapper OBJECT_MAPPER = JsonUtil.getJackson();
#Autowired
private MockMvc mockMvc;
#MockBean
private AdtechService mockService;
#BeforeEach
public void setUp() {
when(mockService.yourMethod(any()).thenReturn(new AdtechResponseDto("123"));
}
#Test
public void testSubmitSession() throws Exception {
// Your original test method
}
}
If the only thing you want to test is your controller you might also want to consider using #WebMvcTest instead of #SpringBootTest.
#WebMvcTest(AdTechController.class)
public class AdtechControllerTest {
private static final ObjectMapper OBJECT_MAPPER = JsonUtil.getJackson();
#Autowired
private MockMvc mockMvc;
#MockBean
private AdtechService mockService;
#BeforeEach
public void setUp() {
when(mockService.yourMethod(any()).thenReturn(new AdtechResponseDto("123"));
}
#Test
public void testSubmitSession() throws Exception {
// Your original test method
}
}
This will load a scaled-down version of the context (only the web parts) and will be quicker to run.
try this:
#Slf4j
#Validated
#RestController
#RequiredArgsConstructor
public class AdtechController {
private AdtechService adtechService;
public AdtechController (AdtechService adtechService) {
this.adtechService= adtechService;
}
#PostMapping(value = "/subscriber/session")
public ResponseEntity<ResponseDto> submitSession(#RequestBody RequestDto requestDto) {
log.trace("execute submitSession with {}", requestDto);
ResponseDtoresponse = adtechService.submitSession(requestDto);
return new ResponseEntity<>(response, HttpStatus.OK);
}
#ExceptionHandler(AdtechServiceException.class)
public ResponseEntity<AdtechErrorResponse> handleAdtechServiceException(AdtechServiceException e) {
return new ResponseEntity<>(new AdtechErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Test:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
#SpringJUnitConfig({AdtechTestConfig.class})
public class AdtechControllerTest {
private static final ObjectMapper OBJECT_MAPPER = JsonUtil.getJackson();
#Autowired
private MockMvc mockMvc;
#Autowired
private AdtechService adtechService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
this.mvc = MockMvcBuilders.standaloneSetup(new AdtechController(adtechService)).build();
}
#Test
public void testSubmitSession() throws Exception {
RequestDto requestDto = new RequestDto ();
requestDto.setKyivstarId("1123134");
requestDto.setMsisdn("123476345242");
requestDto.setPartnerId("112432523");
requestDto.setPartnerName("125798756");
String request = OBJECT_MAPPER.writeValueAsString(requestDto);
System.out.println("REQUEST: " + request);
String response = OBJECT_MAPPER.writeValueAsString(new ResponseDto("123"));
System.out.println("RESPONSE: " + response);
mockMvc.perform(post("/subscriber/session")
.content(MediaType.APPLICATION_JSON_VALUE)
.content(request))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string(containsString(response)));
}
}
Is the AdtechTestConfig.class introducing the /ad-tech path segment in to your test request? If so, this is why your test is trying the path /ad-tech/subscriber/session instead of /subscriber/session.
If this is actually the correct uri, then you may add #RequestMapping to the controller like below or just to the post method itself
#Slf4j
#Validated
#RestController
#RequestMapping("/ad-tech")
#RequiredArgsConstructor
public class AdtechController {
private final AdtechService adtechService;
#PostMapping(value = "/subscriber/session")
public ResponseEntity<ResponseDto> submitSession(#RequestBody RequestDto requestDto) {
...
I want to make a test case with Mock for Spring Boot but i am unable to connect to authorization server:
My Controller:
public class AuthController {
#Autowired
private AuthService authService;
#Autowired
private TokenStore tokenStore;
#PostMapping(value = Constants.LOGIN_URL,
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public Auth login(#RequestBody Auth login, OAuth2Authentication auth) throws ApiException {
Auth result = authService.auth(login);
final OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) auth.getDetails();
result.setAccessToken(details.getTokenValue());
final OAuth2AccessToken accessToken = tokenStore.readAccessToken(details.getTokenValue());
result.setTtl(accessToken.getExpiresIn());
return result;
}
This is My Test, but take an error NullPointer, Maybe is because in the method have a parameter (OAuth2Authentication auth) and i dont know how put this into the test:
#Before
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(controller)
.apply(documentationConfiguration(this.jUnitRestDocumentation))
.setCustomArgumentResolvers(new AuthenticationPrincipalArgumentResolver()).build();
}
#Test
public void getLogin() throws Exception, ApiException {
Auth authMock = Mockito.mock(Auth.class);
Mockito.when(service.auth(Mockito.any(Auth.class))).thenReturn(authMock);
String requestBody = "{" +
"\"username\":" + "\"YENNIFER\"" +
",\"nid\":" + "\"13991676\"" +
",\"password\":" + "\"password\"" +
",\"email\":" + "\"cervecera.artesanal#gmail.com\"" +
"}";
mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody))
.andExpect(status().isOk());
}
You can simply mock AuthService and inject that mock into the controller, e.g.:
#RunWith(SpringJUnit4ClassRunner.class)
public class AuthControllerTest {
#Mock
private AuthService authService;
#InjectMocks
private AuthController controller;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testAuth() {
Auth authMock = Mockito.mock(Auth.class);
Mockito.when(authService.auth(Mockito.any(Auth.class)).thenReturn(auth));
}
}
I use Spring MVC and Spring boot to write a Restful service. This code works fine through postman.While when I do the unit test for the controller to accept a post request, the mocked myService will always initialize itself instead of return a mocked value defined by when...thenReturn... I use verify(MyService,times(1)).executeRule(any(MyRule.class)); and it shows the mock is not used.
I also tried to use standaloneSetup for mockMoc, but it complains it can't find the mapping for the path "/api/rule".
Could anybody help to figure out the problem?
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class MyControllerTest {
#Mock
private MyService myService;
#InjectMocks
private MyController myRulesController;
private MockMvc mockMvc;
#Autowired
private WebApplicationContext wac;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void controllerTest() throws Exception{
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
Long userId=(long)12345;
MyRule happyRule = MyRule.createHappyRule(......);
List<myEvent> mockEvents=new ArrayList<myEvent>();
myEvents.add(new MyEvent(......));
when(myService.executeRule(any(MyRule.class))).thenReturn(mockEvents);
String requestBody = ow.writeValueAsString(happyRule);
MvcResult result = mockMvc.perform(post("/api/rule").contentType(MediaType.APPLICATION_JSON)
.content(requestBody))
.andExpect(status().isOk())
.andExpect(
content().contentType(MediaType.APPLICATION_JSON))
.andReturn();
verify(MyService,times(1)).executeRule(any(MyRule.class));
String jsonString = result.getResponse().getContentAsString();
}
}
Below is my controller class, where MyService is a interface. And I have implemented this interface.
#RestController
#RequestMapping("/api/rule")
public class MyController {
#Autowired
private MyService myService;
#RequestMapping(method = RequestMethod.POST,consumes = "application/json",produces = "application/json")
public List<MyEvent> eventsForRule(#RequestBody MyRule myRule) {
return myService.executeRule(myRule);
}
}
Is api your context root of the application? If so remove the context root from the request URI and test. Passing the context root will throw a 404. If you intend to pass the context root then please refer the below test case. Hope this helps.
#RunWith(MockitoJUnitRunner.class)
public class MyControllerTest {
#InjectMocks
private MyController myRulesController;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = standaloneSetup(myRulesController).build();
}
#Test
public void controllerTest() throws Exception{
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
MyController.User user = new MyController.User("test-user");
ow.writeValueAsString(user);
MvcResult result = mockMvc.perform(post("/api/rule").contentType(MediaType.APPLICATION_JSON).contextPath("/api")
.content(ow.writeValueAsString(user)))
.andExpect(status().isOk())
.andExpect(
content().contentType(MediaType.APPLICATION_JSON))
.andReturn();
}
}
Below is the controller
/**
* Created by schinta6 on 4/26/16.
*/
#RestController
#RequestMapping("/api/rule")
public class MyController {
#RequestMapping(method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
public User eventsForRule(#RequestBody User payload) {
return new User("Test-user");
}
public static class User {
private String name;
public User(String name){
this.name = name;
}
}
}