i'm learning yet testing in java and i'm trying to write an integration test for controller below:
public class OrderController {
#Autowired
private OrderService orderService;
public OrderController(OrderService service) {
this.orderService = service;
}
#PostMapping(value = "/add")
#ApiOperation(value = "Add", response = AddOrderResult.class)
public Response<AddOrderResult> add(#ModelAttribute AddUpdateOrderEnter enter) {
return new Response<>(orderService.addOrder(enter));
}
#PostMapping(value = "/addOrderParts")
#ApiOperation(value = "AddOrderParts", response = GeneralResult.class)
public Response<GeneralResult> AddOrderParts(#ModelAttribute #ApiParam("请求参数") AddOrderPartsEnter enter) {
return new Response<>(orderService.AddOrderParts(enter));
}
#GetMapping(value = "/list")
#ApiOperation(value = "Order List", response = OrderDetailsResult.class)
public Response<List<OrderDetailsResult>> list(#ModelAttribute #ApiParam("请求参数") GeneralEnter enter) {
return new Response<>(orderService.getOrderList(enter));
}
#GetMapping(value = "/details")
#ApiOperation(value = "Order Details", response = OrderDetailsResult.class)
public Response<OrderDetailsResult> details(#ModelAttribute #ApiParam("请求参数") IdEnter enter) {
return new Response<>(orderService.getOrderDetails(enter));
}
#PostMapping(value = "/delete")
#ApiOperation(value = "delete", response = GeneralResult.class)
public Response<GeneralResult> delete(#ModelAttribute IdEnter enter) {
return new Response<>(orderService.deleteOrder(enter));
}
}
My test file which i'm stuck:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
class OrderControllerTest {
#Autowired
private OrderService service;
private MockMvc mockMvc;
#Test
void shouldAddNewOrder() throws Exception {
}
So i want to have some example to how to test my controller in integration way. Thnks for helps :)
What i've done is below:
#RunWith(SpringJUnit4ClassRunner.class)
#WebMvcTest(controllers = OrderControllerTest.class)
class OrderControllerTest {
#MockBean
private OrderService service;
#Autowired
private MockMvc mockMvc;
#Test
void shouldAddNewOrder() throws Exception {
AddUpdateOrderEnter enter = new OrderBuilder()
.orderId(1L)
.orderType(1)
.productId(2L)
.paymentTypeId("STRIPE")
.build();
mockMvc.perform(post("/order/add")
.contentType("application/json"))
.andExpect(status().isOk());
ArgumentCaptor<AddUpdateOrderEnter> orderCaptor = ArgumentCaptor.forClass(AddUpdateOrderEnter.class);
verify(service, times(1)).addOrder(orderCaptor.capture());
assertThat(orderCaptor.getValue().getOrderId()).isEqualTo(1L);
assertThat(orderCaptor.getValue().getOrderType()).isEqualTo(1);
}
Related
I am trying to make testings for my user controller but when I run the test it fails because the response body is empty.
Note that the response code is 200 so i dont see where this problem comes from...
#WebMvcTest(controllers = {UserRestController.class})
public class UserControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
#MockBean
private UserService service;
private MockMvc mockMvc;
private User user1;
private User user2;
#BeforeEach
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
this.user1 = User.builder()
.email("doe.jean#mymel.com")
.username("Djin")
.build();
this.user2 = User.builder()
.email("doe.john#mymel.com")
.username("Jodo")
.build();
}
#Test
public void test_shouldReturnAllUsers() throws Exception {
Mockito.when(service.getUsers(Pageable.unpaged())).thenReturn(new PageImpl<>(Arrays.asList(user1, user2)));
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get(APIPaths.V1_USER_BASE + "/all")
.accept(MediaType.APPLICATION_JSON))
.andExpect(request().asyncStarted())
.andReturn();
mockMvc.perform(asyncDispatch(mvcResult))
.andExpect(status().is(200))
.andExpect(jsonPath("$.size()", Matchers.is(2)))
.andDo(print());
}
}
I have an ApplicationProperties that i use it in my service. When i mock the rest template in my test to test that service it results in java.lang.NullPointerException, when i replace applicationProperties.getBebusiness().getEndPointProvider() with the url, the test passes
#Configuration
#ConfigurationProperties(prefix = "application", ignoreUnknownFields = false)
public class ApplicationProperties { ...
}
#Service
public class ProviderServiceImpl implements ProviderService {
private final ApplicationProperties applicationProperties;
private final RestTemplate restTemplate;
public ProviderServiceImpl(ApplicationProperties applicationProperties, RestTemplate restTemplater) {
this.applicationProperties = applicationProperties;
this.restTemplate = restTemplate;
}
#Override
public List <ProviderDTO> getAllProviderFromBebusiness() {
final List <ProviderDTO> result = new ArrayList<>();
final ResponseEntity <String> responseEntity = restTemplate.getForEntity(
applicationProperties.getBebusiness().getEndPointProvider() + "&page=0&size=" + Integer.MAX_VALUE, String.class);
if (responseEntity.getStatusCodeValue() == 200) {}
return result;
}
}
public class ProviderServiceTest {
#Autowired
private ApplicationProperties applicationProperties;
#Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
#InjectMocks
private ProviderServiceImpl providerService;
#Test
public void givenMockingIsDoneByMockito_whenGetIsCalled_shouldReturnMockedObject() {
String provider = providerInit.buildProviderDTOWithIdFromBebusiness();
ResponseEntity <String> responseEntity = new ResponseEntity <String> (provider, HttpStatus.OK);
when(restTemplate.getForEntity(anyString(), ArgumentMatchers.any(Class.class)))
.thenReturn(responseEntity);
List<ProviderDTO> result = providerService.getAllProviderFromBebusiness();
assertEquals(200, responseEntity.getStatusCodeValue());
}
}
Seems your spring beans are not instantiated. You have to annotate your test class to tell spring it has to initialize its context. Try use #SpringBootTest annotation
You can either use #SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) annotation or if you don't want to run Spring Boot context then just mock your configuration class as following:
#BeforeEach // #Before if you're using JUnit4
void setUp() {
Bebusiness bebusiness = mock(Bebusiness.class);
when(applicationProperties.getBebusiness()).thenReturn(bebusiness);
when(bebusiness.getEndPointProvider()).thenReturn("http://localhost:8080");
}
You can use
#TestPropertySource(locations = "/other-location.properties")
Or
#TestPropertySource(properties = "spring.datasource.max-active=30,
spring.datasource.initial-size=5")
This is a exemple : https://www.logicbig.com/tutorials/spring-framework/spring-core/test-property-source-with-inlined-properties.html
I am new to Unit testing and TDD. I want to apply unit testing for my controller and service class which i have written in spring boot.
I have implemented test class using a tutorial. However, I could not implemented it successfully. I have included my current code.
Controller
#RestController
#RequestMapping("/api")
public class MyController {
private static final Logger LOGGER = LoggerFactory.getLogger(AdminController.class);
#Autowired
MyService myService;
#PostMapping("/create")
public ResponseEntity<?> createUser(#RequestHeader("Authorization") String token,
#RequestBody User user){
ResponseDTO finalResponse = new ResponseDTO();
try {
ResponseEntity<?> entity = myService.create(token, user);
finalResponse.setMessageCode(entity.getStatusCode());
finalResponse.setMessage("Success");
finalResponse.setError(false);
ResponseEntity<ResponseDTO> finalEntity = ResponseEntity.ok().body(finalResponse);
return finalEntity;
} catch (Exception e) {
finalResponse.setMessageCode(HttpStatus.EXPECTATION_FAILED);
finalResponse.setMessage(e.getMessage());
finalResponse.setError(true);
ResponseEntity<ResponseDTO> finalEntity =
ResponseEntity.ok().body(finalResponse);
return finalEntity;
}
}
ResponseDTO
public class ResponseDTO {
private HttpStatus messageCode;
private String message;
private String messageDetail;
private Object body;
private boolean error;
//setters and getters
}
Current Test Class
#RunWith(SpringRunner.class)
public class MyControllerTest {
private MockMvc mockMvc;
#InjectMocks
private MyController myController;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(myController).build();
}
#Test
public void testCreateUser() throws Exception {
mockMvc.perform(post("/api/create")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.*", Matchers.hasSize(1)));
}
}
When i run test class i am getting WARN Resolved [org.springframework.web.bind.MissingRequestHeaderException: Missing request header 'Authorization' for method parameter of type String]
What am i doing wrong here? Any help would be grateful.
Your test could be something like this:
#Test
public void testCreateUser() throws Exception {
mockMvc.perform(post("/api/create")
.accept(MediaType.APPLICATION_JSON)
.header("AUTH_TOKEN", TOKEN)
.content(ObjectToJsonUtil.convertObjectToJsonBytes(user)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.*", Matchers.hasSize(1)));
}
you'll have to convert object user to json.
So you create a util class for this:
public class ObjectToJsonUtil {
public static byte[] convertObjectToJsonBytes(Object object)
throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
JavaTimeModule module = new JavaTimeModule();
mapper.registerModule(module);
return mapper.writeValueAsBytes(object);
}
}
Hope it helps!
There are a few issues with your test:
1. Request mapping
#PostMapping("/create")
public ResponseEntity<?> createUser(
#RequestHeader("Authorization") String token,
#RequestBody User user)
only matches POST requests that have an HTTP header named Authorization and a request body that can be serialized to User. These are not optional. If they are optional, you should explicitly declare that:
#PostMapping("/create")
public ResponseEntity<?> createUser(
#RequestHeader(name = "Authorization", required = false) String token,
#RequestBody(required = false) User user) {
Assuming that they are required, you should setup MockMvc to send both to your controller:
#Test
public void testCreateUser() throws Exception {
mockMvc.perform(
post("/api/create")
.header("Authorization", "XYZ")
.content("{\"firstName\": \"James\", \"lastName\": \"Gosling\"}")
.accept(MediaType.APPLICATION_JSON)
)
.andExpect(status().isCreated())
.andExpect(jsonPath("$.*", Matchers.hasSize(1)));
}
here I have assumed your User class is like this:
public class User {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
2. Content-Type header
Also, you should set the content type header for your MockMvc request, otherwise the test will fail with 415 - Unsupported Media Type. So your test should look like this:
#Test
public void testCreateUser() throws Exception {
mockMvc.perform(
post("/api/create")
.header("Authorization", "XYZ")
.header("Content-Type", "application/json")
.content("{\"firstName\": \"James\", \"lastName\": \"Gosling\"}")
.accept(MediaType.APPLICATION_JSON)
)
.andExpect(status().isCreated())
.andExpect(jsonPath("$.*", Matchers.hasSize(1)));
}
3. Mocked dependencies
Apart from that, in your test you have annotated MyController with #InjectMocks but you haven't mocked its MyService' dependency. That will set themyServicefield of your controller tonull. To fix that you need to mockMyService' too:
#RunWith(SpringRunner.class)
public class MyControllerTest {
private MockMvc mockMvc;
// Mock
#Mock
private MyService myService;
#InjectMocks
private MyController myController;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(myController).build();
}
#Test
public void testCreateUser() throws Exception {
// Configure mock myService
when(myService.create(anyString(), any(User.class))).thenReturn(new ResponseEntity<>(HttpStatus.CREATED));
mockMvc.perform(
post("/api/create")
.header("Authorization", "XYZ")
.header("Content-Type", "application/json")
.content("{\"firstName\": \"James\", \"lastName\": \"Gosling\"}")
.accept(MediaType.APPLICATION_JSON)
)
.andExpect(status().isCreated())
.andExpect(jsonPath("$.*", Matchers.hasSize(1)));
}
}
4. MyService does not satisfy the test conditions
When everything is hunky dory your controller responds with:
ResponseEntity<ResponseDTO> finalEntity = ResponseEntity.ok().body(finalResponse);
which will return a status code of 200. So you either have to modify your test to expect that:
.andExpect(status().isOk())
or you should update your controller to return with the 201 status code:
ResponseEntity<ResponseDTO> finalEntity = ResponseEntity.created(null).body(finalResponse);
I have implemented a rest service similar to this one.
UserController.java
#RestController
#RequestMapping(path = "/user")
public class UserController {
private final UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
#PostMapping(path = "/{id}/avatar")
public void handleUpload(#PathVariable("id") int id, #RequestParam("file") MultipartFile file) {
if (file == null) {
throw new DashboardException("Please select a valid picture");
}
userService.setAvatar(id, file);
}
}
Now I am trying to test the rest endpoint with:
UserControllerEndpointTest.java
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
#WebAppConfiguration
public class UserControllerEndpointTest {
private static final int userId = 42;
private static final String urlPath = String.format("/user/%d/avatar", userId);
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
private UserController controller;
private UserService service;
#Before
public void setUp() throws NoSuchFieldException, IllegalAccessException {
mockMvc = webAppContextSetup(webApplicationContext).build();
service = Mockito.mock(UserService.class);
injectField(controller, "userService", service);
}
#Test
public void successfullySetAvatar() throws Exception {
final InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("test.png");
final MockMultipartFile avatar = new MockMultipartFile("test.png", "test.png", "image/png", inputStream);
doNothing().when(service).setAvatar(userId, avatar);
final MvcResult result = mockMvc.perform(fileUpload(urlPath).file(avatar))
.andExpect(status().isOk())
.andReturn();
verify(service).setAvatar(userId, avatar);
}
}
This fails with 400 - Required request part 'file' is not present.
What am I missing?
Probably you need to change
new MockMultipartFile("test.png", "test.png", "image/png", inputStream);
to
new MockMultipartFile("file", "test.png", "image/png", inputStream);
as the uploaded file parameter name is 'file'
Im trying to test my Spring REST controller but my #Service is always trying to connect to DB.
Controller:
#RestController
#RequestMapping(value = "/api/v1/users")
public class UserController {
private UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<List<User>> getAllUsers() {
List<User> users = userService.findAll();
if (users.isEmpty()) {
return new ResponseEntity<List<User>>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<List<User>>(users, HttpStatus.OK);
}
Test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
#WebAppConfiguration
public class UserControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext wac;
#Before
public void setup() {
this.mockMvc = webAppContextSetup(wac).build();
}
#Test
public void getAll_IfFound_ShouldReturnFoundUsers() throws Exception {
User first = new User();
first.setUserId(1);
first.setUsername("test");
first.setPassword("test");
first.setEmail("test#email.com");
first.setBirthday(LocalDate.parse("1996-04-30"));
User second = new User();
second.setUserId(2);
second.setUsername("test2");
second.setPassword("test2");
second.setEmail("test2#email.com");
second.setBirthday(LocalDate.parse("1996-04-30"));
UserService userServiceMock = Mockito.mock(UserService.class);
Mockito.when(userServiceMock.findAll()).thenReturn(Arrays.asList(first, second));
mockMvc.perform(get("/api/v1/users")).
andExpect(status().isOk()).
andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)).
andExpect(jsonPath("$", hasSize(2))).
andExpect(jsonPath("$[0].userId", is(1))).
andExpect(jsonPath("$[0].username", is("test"))).
andExpect(jsonPath("$[0].password", is("test"))).
andExpect(jsonPath("$[0].email", is("test#email.com"))).
andExpect(jsonPath("$[0].email", is(LocalDate.parse("1996-04-30")))).
andExpect(jsonPath("$[1].userId", is(2))).
andExpect(jsonPath("$[1].username", is("test2"))).
andExpect(jsonPath("$[1].password", is("test2"))).
andExpect(jsonPath("$[1].email", is("test2#email.com"))).
andExpect(jsonPath("$[1].email", is(LocalDate.parse("1996-04-30"))));
verify(userServiceMock, times(1)).findAll();
verifyNoMoreInteractions(userServiceMock);
}
}
My test always failure because instead getting first and second as return, it reads data from DB. If I turn off DB, it throws NestedServletException, nested: DataAccessResourceFailureException.
How can i test it properly? What am I doing wrong?
Mocking userService this way UserService userServiceMock = Mockito.mock(UserService.class); will not inject it into the controller. Remove this line and inject userService as follows
#MockBean UserService userServiceMock;
As #M.Deinum suggested you can remove manual creation of MockMvc and autowired it
#Autowired
private MockMvc mockMvc;
At the end your code should look like
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
#WebAppConfiguration
public class UserControllerTest {
#MockBean
UserService userServiceMock;
#Autowired
private MockMvc mockMvc;
#Test
public void getAll_IfFound_ShouldReturnFoundUsers() throws Exception {
User first = new User();
first.setUserId(1);
first.setUsername("test");
first.setPassword("test");
first.setEmail("test#email.com");
first.setBirthday(LocalDate.parse("1996-04-30"));
User second = new User();
second.setUserId(2);
second.setUsername("test2");
second.setPassword("test2");
second.setEmail("test2#email.com");
second.setBirthday(LocalDate.parse("1996-04-30"));
Mockito.when(userServiceMock.findAll())
.thenReturn(Arrays.asList(first, second));
mockMvc.perform(get("/api/v1/users")).
andExpect(status().isOk()).
andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)).
andExpect(jsonPath("$", hasSize(2))).
andExpect(jsonPath("$[0].userId", is(1))).
andExpect(jsonPath("$[0].username", is("test"))).
andExpect(jsonPath("$[0].password", is("test"))).
andExpect(jsonPath("$[0].email", is("test#email.com"))).
andExpect(jsonPath("$[0].email", is(LocalDate.parse("1996-04-30")))).
andExpect(jsonPath("$[1].userId", is(2))).
andExpect(jsonPath("$[1].username", is("test2"))).
andExpect(jsonPath("$[1].password", is("test2"))).
andExpect(jsonPath("$[1].email", is("test2#email.com"))).
andExpect(jsonPath("$[1].email", is(LocalDate.parse("1996-04-30"))));
verify(userServiceMock, times(1)).findAll();
verifyNoMoreInteractions(userServiceMock);
}
}