220711_TIL

백승한·2022년 7월 11일
0

Mock object (가짜 객체)?

이상적으로, 각 테스트 케이스는 서로 분리되어야 한다. 이를 위해 가짜 객체(Mock object)를 생성하는 것도 좋은 방법이다.

그냥 ProductService를 쓰면 되지않냐?
-> Repository를 DI 받을 방법이 없기때문에 쓰임용도만 대체할 MockReposiotry를 만들어서 주입받지않고 생성해줘서 사용한다.

  1. 분리 되기 어려운 클래스들

  2. 가짜객체 (Mock object) 를 통한 분리 방법

  • 가짜 객체 (Mock object) 로 분리
  • MockRepository
    • 실제 객체와 겉만 같은 객체!
      • 동일한 클래스명, 함수명
    • 실제 DB 작업은 하지 않음
      • DB 작업이 이뤄지는 것처럼~
      • 테스트를 위해 필요한 결과값을 return
  • 이하 간단히 'mock' (목) 이라고 부르기로 함

Mockito mock 을 사용한 단위 테스트

  • Mockito framework: Mock 객체를 쉽게 만들 수 있는 방법 제공. 결과를 명시해주면 해결!
@ExtendWith(MockitoExtension.class)
class ProductServiceTest {
    @Mock
    ProductRepository productRepository;

    @Test
    @DisplayName("관심 상품 희망가 - 최저가 이상으로 변경")
    void updateProduct_Normal() {
    }

스프링 부트를 이용한 통합 테스트

  • 스프링 부트 이용한 통합 테스트
    • 통합 테스트
      • 여러 단위 테스트를 하나의 통합된 테스트로 수행
      • Controller → Service → Repository
    • 단위 테스트 시 스프링은 동작 안 됨 예제 코드)
      class ProductTest {
      
          @Autowired
          ProductService productService;
      
      		// ...
      
      		@Test
          @DisplayName("정상 케이스")
          void createProduct_Normal() {
      				// ...
      
      				****// 에러 발생! productService 가 null
              Product productByService  = productService.createProduct(requestDto, userId);
    • "@SpringBootTest"
      • 스프링이 동작되도록 해주는 어노테이션!
      • 테스트 수행 시 스프링이 동작함
        • Spring IoC 사용 가능
        • Repository 사용해 DB CRUD 가능
      • End to End 테스트도 가능
        • Client 요청 → Controller → Service → Repository → Client 응답
    • @Order(1), @Order(2), ...
      • 테스트의 순서를 정할 수 있음
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class ProductIntegrationTest {
    @Autowired
    ProductService productService;

    Long userId = 100L;
    Product createdProduct = null;
    int updatedMyPrice = -1;

    @Test
    @Order(1)
    @DisplayName("신규 관심상품 등록")
    void test1() {
    }

스프링 MVC 테스트

  • build.gradle
testImplementation 'org.springframework.security:spring-security-test'
  • test > mvc > MockSpringSecurityFilter
public class MockSpringSecurityFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) {}

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        SecurityContextHolder.getContext()
                .setAuthentication((Authentication) ((HttpServletRequest) req).getUserPrincipal());
        chain.doFilter(req, res);
    }

    @Override
    public void destroy() {
        SecurityContextHolder.clearContext();
    }
}
  • test > mvc > UserProductMvcTest
@WebMvcTest(
        controllers = {UserController.class, ProductController.class},
        excludeFilters = {
                @ComponentScan.Filter(
                        type = FilterType.ASSIGNABLE_TYPE,
                        classes = WebSecurityConfig.class
                )
        }
)
class UserProductMvcTest {
    private MockMvc mvc;

    private Principal mockPrincipal;

    @Autowired
    private WebApplicationContext context;

    @Autowired
    private ObjectMapper objectMapper;

    @MockBean
    UserService userService;

    @MockBean
    KakaoUserService kakaoUserService;

    @MockBean
    ProductService productService;
    
    @BeforeEach
    public void setup() {
        mvc = MockMvcBuilders.webAppContextSetup(context)
                .apply(springSecurity(new MockSpringSecurityFilter()))
                .build();
    }

    private void mockUserSetup() {
        // Mock 테스트 유져 생성
        String username = "제이홉";
        String password = "hope!@#";
        String email = "hope@sparta.com";
        UserRoleEnum role = UserRoleEnum.USER;
        User testUser = new User(username, password, email, role);
        UserDetailsImpl testUserDetails = new UserDetailsImpl(testUser);
        mockPrincipal = new UsernamePasswordAuthenticationToken(testUserDetails, "", testUserDetails.getAuthorities());
    }

    @Test
    @DisplayName("로그인 view")
    void test1() throws Exception {
        // when - then
        mvc.perform(get("/user/login"))
                .andExpect(status().isOk())
                .andExpect(view().name("login"))
                .andDo(print());
    }
profile
방문해주셔서 감사합니다🙂

0개의 댓글