When I make a POST method.
This time I will organize POST method developing and testing process. I am going to do 2 types of test which are ui-layer(repository) and application-layer(service).
1. create() on ‘ControllerTest’ prior to ‘Controller’
:: key words ::
- post
- status().isCreated()
- header().string(“location”, “url”)
@Test
public void create() throws Exception {
mvc.perform(post("/restaurants"))
.andExpect(status().isCreated())
.andExpect(header().string("location", "restaurants/1234"))
.andExpect(content().string("{}"));
}
2. Embody @PostMapping on Controller
:: cautions ::
- Set type as ResponseEntity<?>, not as Restaurant because I can’t be sure of which type I’ll receive.
- Unify both url locations in test and test-subject. In this case, I randomly set ‘restaurants/1234’.
@PostMapping("/restaurants")
public ResponseEntity<?> create() throws URISyntaxException {
URI location = new URI("/restaurants/1234");
return ResponseEntity.created(location).body("{}");
}
— — — — — — — — — — — passed
Now, first test that test-subject creates a new post“{}” has been done.
3. Create one object in test
I randomly create a new object in the test at first. I’m using @MockBean on top, so select verify instead of Assert. ((addRestaurant method return nothing now.))
@MockBean
private RestaurantService restaurantService;...@Test
public void create() throws Exception {
Restaurant restaurant = new Restaurant();
restaurant.setId(1234L);
restaurant.setName("New01");
restaurant.setAddress("New01");
mvc.perform(post("/restaurants"))
.andExpect(status().isCreated())
.andExpect(header().string("location", "restaurants/1234"))
.andExpect(content().string("{}"));
verify(restaurantService).addRestaurant(restaurant);
}public void addRestaurant(Restaurant restaurant) {
//
}
4. Create one object in test-subject as well
:: cautions ::
- I’ve got an object and URI location has started to utilize .getId
@PostMapping("/restaurants")
public ResponseEntity<?> create() throws URISyntaxException {
Restaurant restaurant = new Restaurant();
restaurant.setId(1234L);
restaurant.setName("New01");
restaurant.setAddress("New01");
restaurantService.addRestaurant(restaurant);
URI location = new URI("/restaurants/1234");
return ResponseEntity.created(location).body("{}");
}
I got error as long as create objects on both of test and test-subject because it didn’t catch them identically.
Therefore, I delete the object that I created first at test and utilize ‘any()’. It’s going to check whether restaurantService adds something or not.
Remain it at Controller Class.
:: key point ::
- any() in test Class
@Test
public void create() throws Exception {
mvc.perform(post("/restaurants"))
.andExpect(status().isCreated())
.andExpect(header().string("location", "restaurants/1234"))
.andExpect(content().string("{}"));
verify(restaurantService).addRestaurant(any());
}
Also, I want to apply post-content which is new input resource. Be careful of JSON “\”\…
@Test
public void create() throws Exception {
mvc.perform(post("/restaurants")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"id\":\"1234\",\"name\":\"New01\",\"address\":\"New01\"}"))
// new POST resource .andExpect(status().isCreated())
.andExpect(header().string("location", "/restaurants/1234"))
.andExpect((content().string("{}")));
verify(restaurantService).addRestaurant(any());
}
— — — — — — — — — — — passed
Second test that test-subject creates new object has been done. I finished put a create() method in controller which is ui-layer.
However, it doesn’t apply to database in real. Let’s handle real repository to activate ‘create()==save’.
5. Modify parameter
I’ll get a new resource from client and I modify some object factors that I put in person. I used return-type ‘ResponseEntity<?>’. On the other hands, I set type ‘Restaurant’ in parameter.
- from “1234L” to resource.getId()
- from “New01” to resource.getName()
- from “New01” to resource.getAddress()
:: key words ::
- @RequestBody
@PostMapping("/restaurants")
public ResponseEntity<?> creation(@RequestBody Restaurant resource) throws URISyntaxException {
Restaurant restaurant = new Restaurant();
restaurant.setId(resource.getId());
restaurant.setName(resource.getName());
restaurant.setAddress(resource.getAddress()); restaurantService.addRestaurant(restaurant); URI location = new URI("/restaurants/"+restaurant.getId());
return ResponseEntity.created(location).body("{}");
}
6. activate addRestaurant() in RestaurantService
In previous steps, I set that addRestaurant() returns nothing so it just did creating activity which was’t applied in database.
Then I’m going to activate it on RestaurantService.java Class and test it at the same time.
:: cautions ::
- Temporarily set all new restaurant id ‘5678’.
public Restaurant addRestaurant(Restaurant r) {
r.setId(5678L); //set all new restaurant id '5678'
return r;
}
If I create new object through a test, it will be added into RestaurantService.java (repository) and it’s going to set id ‘5678’ by addRestaurant() method.
@Test
public void addRestaurant() {
Restaurant r = new Restaurant();
r.setName("Real-new-post");
r.setAddress("Real-new-place");
Restaurant createdinRS = restaurantService.addRestaurant(r); assertThat(createdinRS.getId(), is(5678L));
}
and it finally turns out createdinRS which is set by ‘5678’, ‘Real-new-post’, ‘Real-new-place’.
— — — — — — — — — — — passed
New test that a new object is saved in RestaurantService repository successfully has been done.
7. Not RestaurantService, but I have to put it in RestaurantRepository!
There is only RestaurantRepository interface in this project. I integrated 2 repositories into service. And service test should cover it which means that restaurant data will be saved in restaurant repository, not in service.
I tried to inject restaurant-repository between restaurant-service and service-test.
- from ‘r.setId(5678L)’ to ‘R savedinRR = rR.save(r)’
- from ‘return r;’ to ‘return savedinRR;’
public Restaurant addRestaurant(Restaurant r) {
Restaurant savedinRR = restaurantRepository.save(r);
return savedinRR;
}
Of course, I should make save() in RestaurantRepository interface in coincidence.
@Repository
public interface RestaurantRepository extends JpaRepository<Restaurant, Long> { Restaurant save(Restaurant restaurant);}
Now, I changed addRestaurant() method to utilize RestaurantRepository to save instead of RestaurantService and data loading process would be changed.
- step1. Create a new object in service test
- step2. Save it in Restaurant Repository
- step3. load it by Mockito ‘given’
- step4. Create and save another object in Service Repository through addRestaurant()
- step5. Check last object compared to first object’s id
I finally finish activate application layer!
👩🏻💻click here to see all project files ➡➡ https://github.com/jyuunnii/SpringBoot_project_eatgo.git