When I make update, PUT and PATCH method.
This time I will organize PUT/PATCH method developing and testing process. In this practice, I used PATCH annotation. Last time that I tried to make POST method, I was challenging in ‘content’. I mean, new resource which is JSON type, has to be qualified at all. For example, 3 fields in one object have to be filled with all. Like below,
.content(“{\”id\”:\”1234\”,\”name\”:\”New01\”,\”address\”:\”New01\”}”))
However, PUT and PATCH was quite different. It doesn’t need to be complete. Let’s start it.
1. update() in ‘Controller Test’
:: key points ::
- patch
- no header()
- content(updating things…)
- status().isOk()
@Test
public void update() throws Exception {
mvc.perform(patch("/restaurants/1001")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"name\":\"update-post\",\"address\":\"update-place\"}"))
.andExpect(status().isOk());
}

2. @PatchMapping on Controller which is test-subject
:: key points ::
- 2 types of parameters
- @PathVariable & @RequestBody
@PatchMapping("/restaurants/{id}")
public String update(@PathVariable("id") Long id,
@RequestBody Restaurant restaurant){
return "{}";
}

— — — — — — passed
I’ve tested that controller updates something “{}” right now.
3. Activate updateRestaurant() in RestaurantService to implement real updating.
:: making process ::
- Now, it became a rule that whatever I try, test set is first to do!
- which name is good for method? ‘updateRestaurant’ sounds good
- use verify
- updateRestaurant in red => let’s go to Repository Class to make it
@Test
public void update() throws Exception {
mvc.perform(MockMvcRequestBuilders.patch("/restaurants/1001")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"name\":\"update-post\",\"address\":\"update-place\"}"))
.andExpect(status().isOk());
verify(restaurantService).updateRestaurant(1001L, "update-post", "update-place");
}

- create updateRestaurant() method in service repository temporarily
- what means for ‘temporarily’? I am using restaurantService right now, so it would be more clear.
public void updateRestaurant(long id, String name, String address) {
//temporary!
}

- finish test and next turn is fixing controller
@PatchMapping("/restaurants/{id}")
public String update(@PathVariable("id") Long id,
@RequestBody Restaurant restaurant){ String name = resource.getName();
String address= resource.getAddress();
restaurantService.updateRestaurant(id, name, address); return "{}";
}

— — — — — — passed
Second test that 3 things (test, repo, controller) synchronize well has been done.
However, method returns nothing because it didn’t apply to database in real. Let’s handle real repository to activate ‘update()==update’.
4. Real update in database
Let’s go to service test as normal. I just set update returns nothing before. Underneath test proves its running.
@Test
public void updateRestaurant(){
Restaurant before = new Restaurant();
before.setId(1004L);
before.setName("post-before");
before.setAddress("place-before");
Restaurant after = restaurantService.updateRestaurant(1004L, "post-after","place-after");
assertThat(after.getName(),is("post-after"));
}

okay,, go to updateRestaurant() subject.. to solve red sign
Make it clear to update() in service repository return ‘restaurant object’.
public Restaurant updateRestaurant(long id, String name, String address) {
Restaurant restaurant = new Restaurant();
restaurant.setId(id);
restaurant.setName(name);
restaurant.setAddress(address);
return restaurant;
}

— — — — — — passed
Third test that data in repository is updated worked.
5. Not RestaurantService, but I have to put it in RestaurantRepository
It updated the object which was created in test and it didn’t through repository. It just used service repo!
I have to interact service repository and restaurant repository.
:: key points ::
- select data by findById in restaurant repository
@Test
public void updateRestaurant(){
Restaurant before = new Restaurant();
before.setId(1004L);
before.setName("post-before");
before.setAddress("place-before");
given(restaurantRepository.findById(1004L)).willReturn(Optional.of(before));
restaurantService.updateRestaurant(1004L, "post-after", "place-after");
assertThat(before.getName(),is("post-after"));
}

— — — — — — — failed : it didn’t change anything because update() method didn’t clear what it had to change.

6.Update activation in RestaurantService
public Restaurant updateRestaurant(long id, String name, String address) {
Restaurant restaurant = restaurantRepository.findById(id).orElse(null);
restaurant.setName(name);
restaurant.setAddress(address);
return restaurant;
}

— — — — — — passed
Forth test that service repo interacted with restaurant repo.
7. Lastly apply to database
:: key points ::
- @Transactional
@Transactional
public Restaurant updateRestaurant(long id, String name, String address) {
Restaurant restaurant = restaurantRepository.findById(id).orElse(null);
restaurant.setName(name);
restaurant.setAddress(address);
return restaurant;
}
— — — — — — passed

++ I got question “when do I have to use given() …?”
I integrated 2 repositories into 1 service repository as service in previous step. It means that when I do service-related thing such as serviceTest, it’s better to utilize ‘mock object’ instead of real one. (Dependency Injection)
In this case, service repository is main thing to check in service test and I have to set others as mock objects to focus on service-related things. I have to cite them in smaller range, I mean @Test, by given().
repository에 대한 처리는 mock object로 만들기 -> RestaurantService 에만 집중
I refer to Mockito setting below.
public class RestaurantServiceTest {
private RestaurantService restaurantService;
@Mock
private RestaurantRepository restaurantRepository;
@Mock
private MenuItemRepository menuItemRepository;
@Before
public void setUp(){
MockitoAnnotations.initMocks(this); // refer to upper @Mock
mockRestaurantRepository(); //restaurant
mockMenuItemRepository(); //menuItem
restaurantService = new RestaurantService(restaurantRepository, menuItemRepository);
}
private void mockRestaurantRepository() {
List<Restaurant> restaurants = new ArrayList<>();
Restaurant restaurant = new Restaurant();
restaurant.setId(1001L);
restaurant.setName("Vegetarian foods");
restaurant.setAddress("Seoul");
restaurants.add(restaurant);
given(restaurantRepository.findAll())
.willReturn(restaurants);
given(restaurantRepository.findById(1001L))
.willReturn(Optional.of(restaurant)); }
private void mockMenuItemRepository() {
List<MenuItem> menuItems = new ArrayList<>();
MenuItem menuItem = new MenuItem();
menuItem.setName("cream-pasta");
menuItems.add(menuItem);
given(menuItemRepository.findAllByRestaurantId(1001L))
.willReturn(menuItems);
}...}
It represents that I directly use RestaurantService repository only.

👩🏻💻click here to see all project files ➡➡ https://github.com/jyuunnii/SpringBoot_project_eatgo.git