-
Mock
Mock이란?
단위 테스트로 사용한다.
실제 객체를 만들어 사용하기에 시간, 비용 등의 Cost가 높거나 혹은 객체 서로간의 의존성이 강해 구현하기 힘들 경우 가짜 객체를 만들어 사용하는 방법이다.
Mock 객체는 언제 필요한가?
- 테스트 작성을 위한 환경 구축이 어려운 경우
- 테스트가 특정 경우나 순간에 의존적인 경우
- 테스트 시간이 오래 걸리는 경우
- 개인 PC의 성능이나 서버의 성능문제로 오래 걸릴수 있는 경우 시간을 단축하기 위해 사용한다.
Mock 객체(Mock Object)
- 행위를 검증하기 위해 사용되는 객체를 지칭하며 수동으로 만들 수도 있고 프레임워크를 통해 만들 수 있다.
- 행위 기반 테스트는 복잡도나 정확성등 작성하기 어려운 부분이 많기 때문에 상태 기반 테스트가 가능하다면 만들지 않는다.
- Mock 객체는 테스트 더블 하위객체로 써의 좁은 의미와 테스트 더블을 포함한 넓은 의미 2가지로 사용 될 수 있다.
출처: https://www.crocus.co.kr/1555 [Crocus]
maven dependency
>> pom.xml
<scope>test</scope> : 범위가 test 인 애한테만 작동 = >
아래에서 나오는 war 파일로 export 하여 배포시 해당 war 파일에는 이 라이브러리가 webapp - lib 폴더에 없다!
<!-- unit test 1 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.6.0</version> <scope>test</scope> </dependency> <!-- unit test 2 --> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-junit-jupiter</artifactId> <version>3.8.0</version> <scope>test</scope> </dependency> <!-- unit test 3 unit test 시 트랜잭션이 문제되나??? --> <dependency> <groupId>org.springframework</groupId>//트랜잭션을 직접 사용하지는 않지만, 알아서 사용하는지 없다면 오류남 <artifactId>spring-tx</artifactId> <version>5.0.2.RELEASE</version> </dependency> <!-- unit test 4 The type org.hamcrest.Matcher cannot be resolved. It is indirectly referenced from required .class files 방지 --> <dependency> <groupId>org.hamcrest</groupId>//필수항목은 아니지만 없을 시 오류남 <artifactId>hamcrest</artifactId> <version>2.2</version> <scope>test</scope> </dependency>
Mock 실습
@Controller public class MemberController { //@Autowired(required=false):Test때 IMemberService의 Mock이 나중에 Injection 되기때문에 일단 (required=false)인체로 생성이 가능하도록 : 더 좋은 방법이 필요하다. @Autowired(required=false) IMemberService memberService; @GetMapping("member") public String 회원등록준비하다() { return "회원등록창"; } @PostMapping("member") public ModelAndView 회원등록하다(Member member) { int 새로등록된회원번호 = memberService.save(member); ModelAndView mv = new ModelAndView(); mv.addObject("no", 새로등록된회원번호); mv.addObject("name", member.getName()); mv.setViewName("등록알림창"); return mv; } @GetMapping("member/{no}") public ModelAndView 회원상세정보를출력하다(@PathVariable int no) { Member 조회된회원 = memberService.findByNo(no); ModelAndView mv = new ModelAndView(); mv.addObject("member", 조회된회원); mv.setViewName("회원상세창"); return mv; } @GetMapping("members") public ModelAndView 회원목록을출하다() { List<Member> 조회된회원들 = memberService.selectAll(); ModelAndView mv = new ModelAndView(); mv.addObject("members", 조회된회원들); mv.setViewName("회원목록창"); return mv; } }
>> 테스트코드
@ExtendWith({SpringExtension.class, MockitoExtension.class}) @ContextConfiguration(classes = {MvcConfig.class}) //테스트에 사용할 컨텍스트 설정을 담은 클래스 혹은 xml 파일 @WebAppConfiguration public class MemberControllerTest { @Mock private IMemberService memberService; @InjectMocks private MemberController memberController; private MockMvc mockMvc; //@Autowired //private WebApplicationContext webApplicationContext; @BeforeEach void setUp() {//매 테스트 단위 마다 mockMvc = MockMvcBuilders.standaloneSetup(memberController) .addFilter(new CharacterEncodingFilter("UTF-8", true)) .build(); //mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); } @Test @DisplayName("MemberController 회원등록준비") public void 회원등록준비_Test() throws Exception{ mockMvc.perform(get("/member")) .andExpect(status().isOk()) .andExpect(MockMvcResultMatchers.model().size(0)) .andExpect(view().name("회원등록창")); } @Test public void 회원을등록하다_Test() throws Exception{ Mockito.when(memberService.save(Mockito.any())).thenReturn(4); //when : 언제, any : 무엇을 넣어도 MockMvcResultMatchers.model().attribute() 안의 값으로 해준다. MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); params.add("name", "name1"); mockMvc.perform(post("/member").params(params)) .andExpect(status().isOk()) .andExpect(view().name("등록알림창")) .andExpect(MockMvcResultMatchers.model().attribute("name", "name1")) .andExpect(MockMvcResultMatchers.model().attribute("no", 4)); } @Test public void 회원상세정보를출력하다_Test() throws Exception{ Member member1 = new Member(); member1.setNo(1); member1.setName("name1"); Mockito.when(memberService.findByNo(1)).thenReturn(member1); Map<String, Object> model= mockMvc.perform(get("/member/1")) .andExpect(status().isOk()) .andExpect(view().name("회원상세창")) .andReturn().getModelAndView().getModel(); Member aMember = (Member)model.get("member"); Assertions.assertEquals(aMember.getName(),"name1"); } @Test public void 회원목록을출력하다_Test() throws Exception{ List<Member> members = new ArrayList<Member>(); Member member1 = new Member(); member1.setNo(1); member1.setName("name1"); members.add(member1); Member member2 = new Member(); member2.setNo(2); member2.setName("name2"); members.add(member2); Mockito.when(memberService.selectAll()).thenReturn(members); mockMvc.perform(get("/members")) .andExpect(status().isOk()) .andExpect(view().name("회원목록창")) .andExpect(model().attribute("members", members)); } }
@ExtendWith({SpringExtension.class, MockitoExtension.class}) //@ExtendWith = 라이브러리들의 이 클래스들을 같이 쓴다는 의미 // MockitoExtension= Mock 을 알아서 처리해주는 라이브러리의 클래스 @Mock 이나 @InjectMocks 가 클래스에 있어야 사용가능 //@Mock = 가짜객체를 만들어 줌 //@InjectMocks = 위의 @Mock 을 알아서 Inject 즉, 주입해준다. //@Test = 테스트메서드 선언 , 무조건 예외 throws 해야 함 //@BeforeEach = 매 테스트 단위 마다 실행(가짜 컨트롤러 메서드 사용 때마다 작동)
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; //MockMvcRequestBuilders 클래스의 모든 메서드 import (static 메서드를 클래스명 생략하고 사용가능) import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; //MockMvcResultMatchers 클래스의 모든 메서드 import (static 메서드를 클래스명 생략하고 사용가능)
'JAVA' 카테고리의 다른 글
java환경변수,tomcat웹배포,mysql다운로드 (0) 2021.08.04 VO 와 DTO (0) 2021.08.03 websocket:채팅 (0) 2021.07.27 3Tier: 예외처리,@Qualifier (0) 2021.07.27 Spring:interceptor,3Tier(Presentation,Business,Dataservice) (0) 2021.07.27