오늘은 Mock Object (모의객체, 가짜객체)에 대해 공부해보았다.
모의 객체
모의 객체(Mock Object)란 주로 객체 지향 프로그래밍으로 개발한 프로그램을 테스트 할 경우 테스트를 수행할 모듈과 연결되는 외부의 다른 서비스나 모듈들을 실제 사용하는 모듈을 사용하지 않고 실제의 모듈을 "흉내"내는 "가짜" 모듈을 작성하여 테스트의 효용성을 높이는데 사용하는 객체이다. 사용자 인터페이스(UI)나 데이터베이스 테스트 등과 같이 자동화된 테스트를 수행하기 어려운 때 널리 사용된다.
유용성
모의 객체를 이용한 테스트는 다음과 같은 상황을 테스트 하는데 유용하게 사용된다.
-
사용자 인터페이스(UI) 테스트 : 사용자의 반응이 필요한 테스트를 수행할 경우 사용자가 테스트에 참여해야 하기 때문에 자동화된 테스트를 수행하기가 어렵다. 이럴 경우 모의 객체를 이용해 사용자의 응답을 흉내내어 사용자의 개입 없이도 테스트를 수행할 수 있다.
-
데이터베이스 테스트 : 자료의 변경을 수반하는 데이터베이스에 대한 작업을 테스트 하는 경우 테스트 수행 후 매번 데이터베이스의 자료를 원래대로 돌려놔야 하는데 이럴 경우 모의 객체를 이용해 데이터베이스의 응답을 흉내내어 데이터의 변경 없이 테스트가 가능하다.
테스트 주도 개발과의 관련성
테스트를 통해 개발을 이끌어나가는 방식인 테스트 주도 개발(Test Driven Developement, TDD) 에서는 자동화된 테스트가 필수적인 요소중의 하나이다. 모의 객체를 이용하면 상당 부분의 테스트를 사용자의 개입 없이 자동화 할 수 있다.
출처: 위키백과
우리 프로젝트 중 우리가 테스트하고자 하는 것 외에 여러개의 객체가 동시에 주입되는데, 이럴 경우 테스트에 집중하기 힘들다.
이럴 때 사용하는 것이 Mock Object이다.
가짜 객체는 나쁜뜻이 아니라 남과 똑같이 행동한다는 의미이다.
스프링은 Mockito 라는 Mock Object 를 지원하고 있다.
스프링은 기본적으로 전통적인 자바 오브젝트를 사용할 것을 권장한다.
그래서 Mockito와 같은 라이브러리들을 지원하는 것.
* RestaurantControllerTest
package kr.co.fastcampus.eatgo.interfaces;
import kr.co.fastcampus.eatgo.application.RestaurantService;
import kr.co.fastcampus.eatgo.domain.MenuItem;
import kr.co.fastcampus.eatgo.domain.Restaurant;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.core.StringContains.containsString;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@WebMvcTest(RestaurantController.class)
public class RestaurantControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
private RestaurantService restaurantService;
@Test
public void list() throws Exception {
List<Restaurant> restaurants = new ArrayList<>();
restaurants.add(new Restaurant(1004L, "JOKER House", "Seoul"));
given(restaurantService.getRestaurants()).willReturn(restaurants);
mvc.perform(get("/restaurants"))
.andExpect(status().isOk())
.andExpect(content().string(
containsString("\"id\":1004")
))
.andExpect(content().string(
containsString("\"name\":\"JOKER House\"")
));
}
@Test
public void detail() throws Exception {
Restaurant restaurant1 = new Restaurant(1004L, "JOKER House", "Seoul");
restaurant1.addMenuItem(new MenuItem("Kimchi"));
Restaurant restaurant2 = new Restaurant(2020L, "Cyber Food", "Seoul");
given(restaurantService.getRestaurant(1004L)).willReturn(restaurant1);
given(restaurantService.getRestaurant(2020L)).willReturn(restaurant2);
mvc.perform(get("/restaurants/1004"))
.andExpect(status().isOk())
.andExpect(content().string(
containsString("\"id\":1004")
))
.andExpect(content().string(
containsString("\"name\":\"JOKER House\"")
))
.andExpect(content().string(
containsString("Kimchi")
));
mvc.perform(get("/restaurants/2020"))
.andExpect(status().isOk())
.andExpect(content().string(
containsString("\"id\":2020")
))
.andExpect(content().string(
containsString("\"name\":\"Cyber Food\"")
));
}
}
*RestaurantServiceTest
package kr.co.fastcampus.eatgo.application;
import kr.co.fastcampus.eatgo.domain.MenuItem;
import kr.co.fastcampus.eatgo.domain.MenuItemRepository;
import kr.co.fastcampus.eatgo.domain.Restaurant;
import kr.co.fastcampus.eatgo.domain.RestaurantRepository;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given;
public class RestaurantServiceTest {
private RestaurantService restaurantService;
@Mock
private RestaurantRepository restaurantRepository;
@Mock
private MenuItemRepository menuItemRepository;
@Before
public void setUp(){
MockitoAnnotations.initMocks(this);
mockRestaurantRepository();
mockMenuItemRepository();
restaurantService = new RestaurantService(
restaurantRepository, menuItemRepository);
}
private void mockMenuItemRepository() {
List<MenuItem> menuItems = new ArrayList<>();
menuItems.add(new MenuItem("Kimchi"));
given(menuItemRepository.findAllByRestaurantId(1004L))
.willReturn(menuItems);
}
private void mockRestaurantRepository() {
List<Restaurant> restaurants = new ArrayList<>();
Restaurant restaurant = new Restaurant(1004L,"Bob zip", "Seoul");
restaurants.add(restaurant);
given(restaurantRepository.findAll()).willReturn(restaurants);
given(restaurantRepository.findById(1004L)).willReturn(restaurant);
}
@Test
public void getRestaurants(){
List<Restaurant> restaurants = restaurantService.getRestaurants();
Restaurant restaurant = restaurants.get(0);
assertThat(restaurant.getId(), is(1004L));
}
@Test
public void getRestaurant(){
Restaurant restaurant = restaurantService.getRestaurant(1004L);
assertThat(restaurant.getId(), is(1004L));
MenuItem menuItem = restaurant.getMenuItems().get(0);
assertThat(menuItem.getName(), is("Kimchi"));
}
}
*RestaurantService
package kr.co.fastcampus.eatgo.application;
import kr.co.fastcampus.eatgo.domain.MenuItem;
import kr.co.fastcampus.eatgo.domain.MenuItemRepository;
import kr.co.fastcampus.eatgo.domain.Restaurant;
import kr.co.fastcampus.eatgo.domain.RestaurantRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class RestaurantService {
@Autowired
RestaurantRepository restaurantRepository;
@Autowired
MenuItemRepository menuItemRepository;
public RestaurantService(RestaurantRepository restaurantRepository,
MenuItemRepository menuItemRepository) {
this.restaurantRepository = restaurantRepository;
this.menuItemRepository = menuItemRepository;
}
public List<Restaurant> getRestaurants(){
List<Restaurant> restaurants = restaurantRepository.findAll();
return restaurants;
}
public Restaurant getRestaurant(Long id){
Restaurant restaurant = restaurantRepository.findById(id);
List<MenuItem> menuItems = menuItemRepository.findAllByRestaurantId(id);
restaurant.setMenuItems(menuItems);
return restaurant;
}
}
하... 여기까지 베껴쓰는 느낌으로 따라오긴 했는데, 솔직히 전체적인 흐름이 하나도 파악이 안되는 느낌이다.
설명도 좀 부실한 것 같고, 이해가 안되는 채로 따라가려니 너무 재미가 없다...
그래서 스프링 부트 프로젝트(레스토랑 예약 사이트)편은 여기서 잠시 멈추고
강현호 강사님의 강의인 스프링 부트 프로젝트(지인 정보 관리 시스템) 초반부를 먼저 들어보기로 했다.
다행히 설명이 조금 더 풍부한 강의인 것 같다.
당분간 이 강의를 듣고 좀 감이 잡히면 다시 레스토랑 예약 사이트도 도전해봐야겠다.
스프링과 스프링부트의 차이점은 아래와 같다.
아래는 다른 언어로 같은 것을 개발하는 것과 스프링 부트를 써서 개발하는 것의 비교를 통해
스프링부트가 얼마나 간편한지를 보여주는 예시이다.
강의의 핵심을 요약하면 아래와 같다.
패스트캠퍼스 강의: https://bit.ly/3ilMbIO
'언어공부 > JAVA&SPRING' 카테고리의 다른 글
[패스트캠퍼스 수강 후기] 자바 인강 100% 환급 챌린지 27회차 미션 (0) | 2020.09.05 |
---|---|
[패스트캠퍼스 수강 후기] 자바 인강 100% 환급 챌린지 26회차 미션 (0) | 2020.09.04 |
[패스트캠퍼스 수강 후기] 자바 인강 100% 환급 챌린지 24회차 미션 (0) | 2020.09.02 |
[패스트캠퍼스 수강 후기] 자바 인강 100% 환급 챌린지 23회차 미션 (0) | 2020.09.01 |
[패스트캠퍼스 수강 후기] 자바 인강 100% 환급 챌린지 22회차 미션 (0) | 2020.08.31 |
댓글