[01.26] 내일배움캠프[Spring] TIL-59
1. Test Code
- TDD ->
Test Code
먼저 작성하고 실제 로직을 작성하는 방식으로 아직 찬반 여부 갈림.
UserService Test Code
@ExtendWith(MockitoExtension.class)
class UserServiceImplTest {
@Mock
private UserRepository userRepository;
@Mock
private ProfileRepository profileRepository;
@Mock
private PointRepository pointRepository;
@Mock
private JwtUtil jwtUtil;
@InjectMocks
private UserServiceImpl userService;
@Spy
private BCryptPasswordEncoder passwordEncoder;
@Test
@DisplayName("회원 가입성공")
void signup() {
SignupDto signupDto = SignupDto.builder()
.username("asd1234")
.password("asd12345!")
.nickname("일반유저")
.admin(false)
.build();
when(userRepository.findByUsername(any(String.class)))
.thenReturn(Optional.empty());
String msg = userService.signup(signupDto);
assertThat(msg).isEqualTo("회원가입 성공");
}
@Test
@DisplayName("회원 가입실패")
void signupFail() {
SignupDto signupDto = SignupDto.builder()
.username("asd1234")
.password("asd12345!")
.nickname("일반유저")
.admin(false)
.build();
when(userRepository.findByUsername(any(String.class)))
.thenReturn(Optional.ofNullable(User.builder()
.username("asd1234")
.password("asd12345!")
.role(UserRoleEnum.CUSTOMER)
.build())
);
assertThatThrownBy(
() -> userService.signup(signupDto)
).isInstanceOf(IllegalArgumentException.class)
.hasMessage("유저가 존재합니다");
}
@Test
@DisplayName("로그인")
void login() {
LoginUserRequestDto loginUserRequestDto = LoginUserRequestDto.builder()
.username("asd1234")
.password("asd12345!")
.build();
User user = User.builder()
.username("asd1234")
.password(passwordEncoder.encode("asd12345!"))
.role(UserRoleEnum.CUSTOMER)
.build();
MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();
when(userRepository.findByUsername(any(String.class)))
.thenReturn(Optional.of(user));
String msg = userService.login(loginUserRequestDto,mockHttpServletResponse);
String token = jwtUtil.createToken(any(),any());
assertThat(msg).isEqualTo("로그인 성공");
}
}
Problem / Solve
- 회원가입 : 성공 케이스는 어찌저찌 잘 작성했는데, 실패 케이스를 작성할 때
asserThatThrowBy로 IllegalArgumentException()과 그 메세지를 비교할 때 생각보다 시간이 오래 걸렸다.
- 로그인 : 사실 로그인은 그냥 로그인이 잘 되는지 안되는지의 목적이지 Jwt토큰이 잘 발행 되는지 의무를 확인 할 필요는 없다는 사실을 뒤늦게 깨달았다.
- 그리고 JwtToken을 만들 Key를
@PostContstruct
에서 init()
해주는데, @Spy
가 실제 로직을 그대로 실행한다고 해서 사용했지만, 작동하지 않아서 계속 Key값이 들어가지 않았다.
- 기존의 코드를 수정하는 것 이지만 JwtUil에
@PostConstruct
에서 하던 작업을 기본 생성자에 넣어서 해결하는 방법도 있다.
- 다른 분들은
@Spy
해서도 @PostConstruct
가 돌아가게 할 수 있다고 하셨는데, 조금더 공부가 필요한 것 같다.
ProductService Test Code
@ExtendWith(MockitoExtension.class)
class ProductServiceImplTest {
@Mock
private ProductRepository productRepository;
@InjectMocks
private ProductServiceImpl productService;
@Test
@DisplayName("상품추가")
void addProduct() {
ProductRequestDto productResponseDto = new ProductRequestDto();
User user = new User();
String msg = productService.addProduct(productResponseDto,user);
assertThat(msg).isEqualTo("해당 상품을 등록 완료했습니다");
verify(productRepository,times(1)).save(any(Product.class));
}
@Test
@DisplayName("상품 정보 수정 정상")
void updateProduct() {
ProductUpdateRequestDto productUpdateRequestDto = new ProductUpdateRequestDto(7777);
User user = mock(User.class);
Product product = Product.builder().productName("computer").price(1000).build();
given(user.checkUser(anyLong())).willReturn(true);
given(productRepository.findById(anyLong())).willReturn(Optional.ofNullable(product));
String msg = productService.updateProduct(anyLong(),productUpdateRequestDto,user);
assertThat(msg).isEqualTo("해당 제품의 가격이 업데이트 되었습니다");
}
@Test
@DisplayName("상품내역 전체보기")
void showProductList() {
PageDto pageDto = new PageDto(1,1,"id",true);
Pageable pageable = productService.makePage(pageDto);
given(productRepository.findAll(pageable)).willReturn(Page.empty());
Page<ProductResponseDto> products = productService.showProductList(pageDto);
verify(productRepository,times(1)).findAll(pageable);
assertThat(products).isEmpty();
}
}
Problem / Solve
- 상품등록 : 별다른 문제는 없었다. 다만 이 비즈니스 로직을 실행했을 때의 기대 값에 대한 것이 없고 그냥
저장!
이기 때문에, save()
메서드가 1번 실행되는 것이 맞는지 verify()
로 확인했다.
- 상품 정보 수정 : 여기선 아주 큰 깨달음이 있었다.
현재 우리의 TestCode
작업은 기존에 완성했던 프로젝트를 가지고 한다는 사실을 망각했다.
user.checkUser(long)
으로 그 유저가 맞을 시 update()
를 하는 로직인데, 이 것또한 Test해봐야 한다고 생각해서 처음에는 User를 생성자 형태로 변수를 다 할당해서 만들어 줬다.
-> 그랬더니 계속 인식하지 못했고, 오히려 User user = new User(.....)
만! 넣었을 때는 기대 값이 나왔다.
- 해결 : User객체를 Mock으로 만들고, 그 객체를 검증하는 부분 또한 given ~ willReturn으로 줬을 때 비로소 해결했다.
-> 정리 : 객체 자체안에 검증하는 로직이 들어가고 그것을 테스트 해보고싶다면, 그 객체를 Mock
객체로 만들어, 그 검증 로직 또한 선언해주자...!
-> 그냥 User user = new User(.....)
이런식으로 객체를 생성하고 그것으로 테스트한다면, 그냥 검증이 잘 된 상태로 돌아간다!
요약
- 어떨 때는 when ~ ThenReturn / given ~ willReturn ??
-> TDD / BDD 인데 사실 given // when // then// 딴에서 given 구문에 when절이 들어가서 가독성이 떨어지고, 혼란을 야기 했기 때문에 등장..!
- 객체안에 검증이나 다른 로직이 필요할 땐, Mock객체로 만들어서 쓰자..!