深化クラス-3週目-2
2022年4月16日(土)
[スパルタコードクラブ]Spring深化クラス-3週目-2
Mockitoフレームワーク: Mockオブジェクトを簡単に作成する方法例 統合テスト:2つ以上のモジュールの接続状態をテストします.モジュール間の接続を検証できるエラー 設計 "@SpringBootTest":テスト時にスプリングを作動させる方法 Spring IoC、DB CRUDは 利用可能
@Order(1),@Order(2):ソート可能テスト は、デバイステストよりも長い時間を使用します. 例(サービスとレポートを一緒にテスト) 適用事項 例
[スパルタコードクラブ]Spring深化クラス-3週目-2
◇Mockito mockテストユニット使用
@ExtendWith(MockitoExtension.class) // Mockito 사용함
class ProductServiceTest {
@Mock // 해당 repository를 mock으로 생성해서 사용
ProductRepository productRepository;
@Test
@DisplayName("관심 상품 희망가 - 최저가 이상으로 변경")
void updateProduct_Normal() {
// given
Long productId = 100L;
int myprice = MIN_MY_PRICE + 1000;
ProductMypriceRequestDto requestMyPriceDto = new ProductMypriceRequestDto(
myprice
);
Long userId = 777L;
ProductRequestDto requestProductDto = new ProductRequestDto(
"오리온 꼬북칩 초코츄러스맛 160g",
"https://shopping-phinf.pstatic.net/main_2416122/24161228524.20200915151118.jpg",
"https://search.shopping.naver.com/gate.nhn?id=24161228524",
2350
);
Product product = new Product(requestProductDto, userId);
ProductService productService = new ProductService(productRepository);
when(productRepository.findById(productId))
.thenReturn(Optional.of(product));
// when
Product result = productService.updateProduct(productId, requestMyPriceDto);
// then
assertEquals(myprice, result.getMyprice());
}
@Test
@DisplayName("관심 상품 희망가 - 최저가 미만으로 변경")
void updateProduct_Failed() {
// given
Long productId = 100L;
int myprice = MIN_MY_PRICE - 50;
ProductMypriceRequestDto requestMyPriceDto = new ProductMypriceRequestDto(
myprice
);
ProductService productService = new ProductService(productRepository);
// when // exception을 만듦
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
productService.updateProduct(productId, requestMyPriceDto);
});
// then
assertEquals(
"유효하지 않은 관심 가격입니다. 최소 " + MIN_MY_PRICE + " 원 이상으로 설정해 주세요.",
exception.getMessage()
); // 예상값, 실제값
}
}
◇スプリングガイドによる集積テスト
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) // 랜덤 포트로 Spring 실행
@TestInstance(TestInstance.Lifecycle.PER_CLASS) // Class 별 테스트 실행? 구글링 참고
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) // 순차적으로 테스트 실행
class ProductIntegrationTest {
@Autowired // Bean 사용 가능
ProductService productService;
Long userId = 100L;
Product createdProduct = null;
int updatedMyPrice = -1;
@Test
@Order(1) // Order 순서대로 실행
@DisplayName("신규 관심상품 등록")
void test1() {
// given
String title = "Apple <b>에어팟</b> 2세대 유선충전 모델 (MV7N2KH/A)";
String imageUrl = "https://shopping-phinf.pstatic.net/main_1862208/18622086330.20200831140839.jpg";
String linkUrl = "https://search.shopping.naver.com/gate.nhn?id=18622086330";
int lPrice = 77000;
ProductRequestDto requestDto = new ProductRequestDto(
title,
imageUrl,
linkUrl,
lPrice
);
// when
Product product = productService.createProduct(requestDto, userId);
// then
assertNotNull(product.getId());
assertEquals(userId, product.getUserId());
assertEquals(title, product.getTitle());
assertEquals(imageUrl, product.getImage());
assertEquals(linkUrl, product.getLink());
assertEquals(lPrice, product.getLprice());
assertEquals(0, product.getMyprice());
createdProduct = product; // test2 에서 사용할 객체 만들기
}
@Test
@Order(2)
@DisplayName("신규 등록된 관심상품의 희망 최저가 변경")
void test2() {
// given
Long productId = this.createdProduct.getId();
int myPrice = 70000;
ProductMypriceRequestDto requestDto = new ProductMypriceRequestDto(myPrice);
// when
Product product = productService.updateProduct(productId, requestDto);
// then
assertNotNull(product.getId());
assertEquals(userId, product.getUserId());
assertEquals(this.createdProduct.getTitle(), product.getTitle());
assertEquals(this.createdProduct.getImage(), product.getImage());
assertEquals(this.createdProduct.getLink(), product.getLink());
assertEquals(this.createdProduct.getLprice(), product.getLprice());
assertEquals(myPrice, product.getMyprice());
this.updatedMyPrice = myPrice; // test3에서 사용할 객체 만들기
}
@Test
@Order(3)
@DisplayName("회원이 등록한 모든 관심상품 조회")
void test3() {
// given
// when
List<Product> productList = productService.getProducts(userId);
// then
// 1. 전체 상품에서 테스트에 의해 생성된 상품 찾아오기 (상품의 id 로 찾음)
Long createdProductId = this.createdProduct.getId();
Product foundProduct = productList.stream()
.filter(product -> product.getId().equals(createdProductId))
.findFirst()
.orElse(null);
// 2. Order(1) 테스트에 의해 생성된 상품과 일치하는지 검증
assertNotNull(foundProduct);
assertEquals(userId, foundProduct.getUserId());
assertEquals(this.createdProduct.getId(), foundProduct.getId());
assertEquals(this.createdProduct.getTitle(), foundProduct.getTitle());
assertEquals(this.createdProduct.getImage(), foundProduct.getImage());
assertEquals(this.createdProduct.getLink(), foundProduct.getLink());
assertEquals(this.createdProduct.getLprice(), foundProduct.getLprice());
// 3. Order(2) 테스트에 의해 myPrice 가격이 정상적으로 업데이트되었는지 검증
assertEquals(this.updatedMyPrice, foundProduct.getMyprice());
}
}
◇テストスプリングMVC(今回のテストコントローラで使用)
//build.gradle dependency 에 넣고 실행
testImplementation 'org.springframework.security:spring-security-test'
// test > mvc > MockSpringSecurityFilter.js
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();
}
}
@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 = "[email protected]";
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());
}
@Test
@DisplayName("회원 가입 요청 처리")
void test2() throws Exception {
// given
MultiValueMap<String, String> signupRequestForm = new LinkedMultiValueMap<>();
signupRequestForm.add("username", "제이홉");
signupRequestForm.add("password", "hope!@#");
signupRequestForm.add("email", "[email protected]");
signupRequestForm.add("admin", "false");
// when - then
mvc.perform(post("/user/signup")
.params(signupRequestForm)
)
.andExpect(status().is3xxRedirection())
.andExpect(view().name("redirect:/user/login"))
.andDo(print());
}
@Test
@DisplayName("신규 관심상품 등록")
void test3() throws Exception {
// given
this.mockUserSetup();
String title = "Apple <b>에어팟</b> 2세대 유선충전 모델 (MV7N2KH/A)";
String imageUrl = "https://shopping-phinf.pstatic.net/main_1862208/18622086330.20200831140839.jpg";
String linkUrl = "https://search.shopping.naver.com/gate.nhn?id=18622086330";
int lPrice = 77000;
ProductRequestDto requestDto = new ProductRequestDto(
title,
imageUrl,
linkUrl,
lPrice
);
String postInfo = objectMapper.writeValueAsString(requestDto);
// when - then
mvc.perform(post("/api/products")
.content(postInfo)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.principal(mockPrincipal)
)
.andExpect(status().isOk())
.andDo(print());
}
}
◇他各种SpringBoot Annotation的哦
Reference
この問題について(深化クラス-3週目-2), 我々は、より多くの情報をここで見つけました https://velog.io/@gwichanlee/Spring-심화반-3주차-2テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol