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);
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'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);
}
I have catalog service that works with product service to get data (microservices). When I try to make getForObject in catalog service, I have an error 404.
#RestController
#RequestMapping("/catalog")
public class ProductCatalogApi {
#Autowired
private RestTemplate restTemplate;
#GetMapping("")
public String hello(){
return "Heelloooo";
}
#GetMapping("/{category}")
public void getProductsByCategoryName(#PathVariable String category) {
UserProduct userProduct = restTemplate.getForObject(
"http://shop-product-service/shop/products" + category,
UserProduct.class);
System.out.println("dsdasa--------"+ userProduct);
}
This is my product service:
#RestController
#RequestMapping("/shop")
public class ProductController {
#Autowired
ProductRepository productRepository;
#GetMapping("/all")
public List<Product> index(){
return productRepository.findAll();
}
#GetMapping("/product/{id_product}")
public Optional<Product> showByProductId(#PathVariable String id_product){
return productRepository.findById(id_product);
}
#GetMapping("/products/{category}")
public List<Product> showByCategoryName(#PathVariable String category){
return productRepository.findByCategory(category);
}
}
So when I try to make link this: http://localhost:8082/catalog/electronics, I get error, Please help me.
you lost the character "/" in the class ProductCatalogApi :
restTemplate.getForObject("http://shop-product-service/shop/products" + category,
UserProduct.class);
http://shop-product-service/shop/products =>
http://shop-product-service/shop/products/
"Fixed":
My endpoint was returning void instead of the User so it couldn't get the values.
I'm making tests using webTestClient for my application and the POST test has no value at JSON.
Code:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
#AutoConfigureWebTestClient
public class UserResourceTest {
#Autowired
private WebTestClient webTestClient;
#Test
public void testCreateUser() {
User user = new User(1L, "Slavik", 19);
webTestClient.post()
.uri("/api/user/")
.contentType(APPLICATION_JSON_UTF8)
.accept(APPLICATION_JSON_UTF8)
.body(Mono.just(user), User.class)
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.id").isNotEmpty()
.jsonPath("$.username").isEqualTo("Slavik");
}
#Test
public void getAllOkForGetAll() {
webTestClient.get()
.uri("/api/user/")
.accept(APPLICATION_JSON_UTF8)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(APPLICATION_JSON_UTF8)
.expectBodyList(User.class);
}
}
The GET method works.
Removing the both JSON lines makes the test pass.
.expectBody(User.class) does not work.
Expecting status OK is correct for my case (should be Create but it's not the issue)
Removing $. also did not work
The Endpoint:
#PostMapping
#ResponseStatus(value = HttpStatus.OK)
public void add() {
this.userService.add();
}
User class:
#Builder
#Data
#NoArgsConstructor
#AllArgsConstructor
public class User {
private Long id;
private String username;
private Integer age;
}
I have simple Spring boot Rest application what returns Users list from database.
Application works as expected but test scenario fail with error. After long googling cannot figure out why?
It seems that test class cannot access userRepository and instead of calling userRepository.getAllUsers it calls AppController.getAllUsers.
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
……………………………
Caused by: java.lang.NullPointerException
at com.loan.demo.controller.AppController.getAllUsers(AppController.java:43)
…………………………………………..
These are my classes:
LoanAppApplication
#SpringBootApplication
public class LoanAppApplication {
public static void main(String[] args) {
SpringApplication.run(LoanAppApplication.class, args);
}
}
Class User.java
#Entity
#Table(name="USERTABLE")
public class User {
private int id;
#NotNull
private String firstName;
#NotNull
private String lastName;
#NotNull
private String persID;
private int blocked;
private Set<Loan> loans;
public User() {
}
public User(String firstName, String lastName, String persID) {
this.firstName = firstName;
this.lastName = lastName;
this.persID = persID;
}
UserRepository:
#Repository
public interface UserRepository extends JpaRepository<User, Integer>{
public User findById(int Id);
public User findByPersID(String userId);
}
And Rest Controller:
#RestController
public class AppController {
#Autowired
UserRepository userRepository;
#GetMapping("/doit")
public String doIt() {
return "Do It";
}
//list all users
#GetMapping("/users")
public List<User> getAllUsers() {
return userRepository.findAll(); // this is line 43 from debuging error log
}
}
And test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {LoanAppApplication.class})
public class LoanAppApplicationTests {
private MockMvc mockMvc;
#InjectMocks
private AppController appController;
#Before
public void addData() {
mockMvc = MockMvcBuilders.standaloneSetup(appController)
.build();
}
//First test scenario that return only string works perfectly
#Test
public void testData() throws Exception {
mockMvc.perform(get("/doit")
)
.andExpect(status().isOk())
.andExpect(content().string("Do It"));
}
//but second that should return empty json string fails with exception
#Test
public void testGet() throws Exception {
mockMvc.perform(get("/users")
)
.andExpect(status().isOk())
.andExpect(content().string("Do It")); //this test should fail but not return exception
}
}
You need to mock your userRepository
#Mock
UserRepository userRepository;
so after #Mock you need to initialize Mock`s in #Before, add this:
MockitoAnnotations.initMocks(this);
then in code setup what users you want to get
User user = new User();
when(userRepository.getUsers()).thenReturn(Collections.singletonList(user));
and then check
verify(userRepository, times(1)).getUsers();
verifyNoMoreInteractions(userRepository);
this is because you application context is not working