심화주차 개인과제 해설
> 테스트는 우리가 작성한 코드가 의도대로 동작하는지 체크하는데 의미가 있다
(getter, setter 같은 것들은 의미없음)
> 테스트도중 테스트코드 본문에는 단순 리터럴 상수가 보이는건 좋지않다
일반적으로는 interface를 활용해서 사용자정의 상수를 만들고 그것을 테스트코드에 활용
첫번째 테스트(UserRequestDtoTest)
class UserRequestDTOTest implements CommonTest {
@DisplayName("유저 요청 DTO 생성")
@Nested
class createUserRequestDTO {
@DisplayName("유저 요청 DTO 생성 성공")
@Test
void createUserRequestDTO_success() {
// given
UserRequestDTO userRequestDTO = new UserRequestDTO();
userRequestDTO.setUsername("abcd1234");
userRequestDTO.setPassword("Abcdef12");
// when
Set<ConstraintViolation<UserRequestDTO>> violations = validate(userRequestDTO);
// then
assertThat(violations).isEmpty();
}
@DisplayName("유저 요청 DTO 생성 실패 - 잘못된 username")
@Test
void createUserRequestDTO_fail_wrongUserName() {
// given
UserRequestDTO userRequestDTO = new UserRequestDTO();
userRequestDTO.setUsername("Invalid user name"); // Invalid username pattern
userRequestDTO.setPassword(TEST_USER_PASSWORD); // Invalid password pattern
// when
Set<ConstraintViolation<UserRequestDTO>> violations = validate(userRequestDTO);
// then
assertThat(violations).hasSize(1);
assertThat(violations)
.extracting("message")
.contains("\"^[a-z0-9]{4,10}$\"와 일치해야 합니다");
}
@DisplayName("유저 요청 DTO 생성 실패 - 잘못된 password")
@Test
void createUserRequestDTO_wrongPassword() {
UserRequestDTO userRequestDTO = new UserRequestDTO();
userRequestDTO.setUsername(TEST_USER_NAME); // Invalid username pattern
userRequestDTO.setPassword("Invalid password"); // Invalid password pattern
// when
Set<ConstraintViolation<UserRequestDTO>> violations = validate(userRequestDTO);
// then
assertThat(violations).hasSize(1);
assertThat(violations)
.extracting("message")
.contains("\"^[a-zA-Z0-9]{8,15}$\"와 일치해야 합니다");
}
}
private Set<ConstraintViolation<UserRequestDTO>> validate(UserRequestDTO userRequestDTO) {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
return validator.validate(userRequestDTO);
}
}
코드 분석
- implements CommonTest
- 인터페이스에 사용할 상수, 객체등을 정의해두고 본문에서는 정의한 필드명 사용
- private Set<ConstraintViolation<UserRequestDTO>> validate(UserRequestDTO userRequestDTO)
- 기존에 컨트롤러에서 @Valid 를 사용해서 dto값을 검증하는데 테스트코드에선 @Valid를 사용할수가 없다
- @Valid는 Validation 라이브러리의 기능, 라이브러리 기능중 Factory를 반환하는 메서드를 통해 Factory얻음
- Factory에서 Validator를 얻을수 있다
- Validator에서 우리가 원하는 @Valid의 validate() 기능을 제공한다, requestDto안에 선언되어있는 @Pattern @NotNull 등 기존 @Valid에서 검증하는것들 검증가능!
- ConstraintViolation<T> : T내부에서 예외가 발생시, 잘못됐다는 메시지값을 제공한다
- Set인 이유는 @Valid는 bindingResult와 같이 사용할 때처럼 예외에 걸리는 것들을 모두 메시지로 반환해서
- assertThat
- 형식: assertThat(타겟).메서드().메서드()
- 다양한 체인 메서드들이 있어 검색 후 사용하는 방식
두번째 테스트(JwtUtilTest)
@SpringBootTest
@ActiveProfiles("test")
class JwtUtilTest implements CommonTest {
@Autowired
JwtUtil jwtUtil;
@Mock
private HttpServletRequest request;
@BeforeEach
void setUp() {
// 인젝트 Mock을 사용할때 필요한 코드, 현재코드에선 필요x
MockitoAnnotations.openMocks(this);
jwtUtil.init();
}
@DisplayName("토큰 생성")
@Test
void createToken() {
// when
String token = jwtUtil.createToken(TEST_USER_NAME);
// then
assertNotNull(token);
}
@DisplayName("토큰 추출")
@Test
void resolveToken() {
// given
String token = "Bearer test-token";
// when
given(request.getHeader(JwtUtil.AUTHORIZATION_HEADER)).willReturn(token);
String resolvedToken = jwtUtil.resolveToken(request);
// then
assertEquals("test-token", resolvedToken);
}
@DisplayName("토큰 검증")
@Nested
class validateToken {
@DisplayName("토큰 검증 성공")
@Test
void validateToken_success() {
// given
String token = jwtUtil.createToken(TEST_USER_NAME).substring(7);
// when
boolean isValid = jwtUtil.validateToken(token);
// then
assertTrue(isValid);
}
@DisplayName("토큰 검증 실패 - 유효하지 않은 토큰")
@Test
void validateToken_fail() {
// given
String invalidToken = "invalid-token";
// when
boolean isValid = jwtUtil.validateToken(invalidToken);
// then
assertFalse(isValid);
}
}
@DisplayName("토큰에서 UserInfo 조회")
@Test
void getUserInfoFromToken() {
// given
String token = jwtUtil.createToken(TEST_USER_NAME).substring(7);
// when
Claims claims = jwtUtil.getUserInfoFromToken(token);
// then
assertNotNull(claims);
assertEquals(TEST_USER_NAME, claims.getSubject());
}
}
코드 분석
- @ActiveProfiles("test")
- 테스트코드에서 사용할 application.property를 가져온다는 느낌
- application-test.properties 와 같은 이름으로 생성하면 그 내용을 가져와 적용
- jwtUtil 테스트에서는 secretKey값과 jdbc 연결에 필요한 것들을 가져오고있다
- @SpringBootTest 안에서 Mock 사용가능
- 실제 환경처럼 구동하는 SpringBootTest지만 동시에 Mock을 활용한 다양한 케이스적용 가능
- given() 사용시, 실제환경에서 가져온 util의 메서드를 테스트중 해당 결과를 willReturn()값으로 변경해준다
공통 상수 및 객체값을 담은 interface
public interface CommonTest {
String ANOTHER_PREFIX = "another-";
Long TEST_USER_ID = 1L;
Long TEST_ANOTHER_USER_ID = 2L;
String TEST_USER_NAME = "username";
String TEST_USER_PASSWORD = "password";
User TEST_USER = User.builder()
.username(TEST_USER_NAME)
.password(TEST_USER_PASSWORD)
.build();
User TEST_ANOTHER_USER = User.builder()
.username(ANOTHER_PREFIX + TEST_USER_NAME)
.password(ANOTHER_PREFIX + TEST_USER_PASSWORD)
.build();
}
'TIL > Web Back' 카테고리의 다른 글
[Sparta] 내일배움캠프 팀프 TIL 1일차 (1) | 2024.06.19 |
---|---|
[Sparta] 내일배움캠프 TIL 35일차 (0) | 2024.06.18 |
[Sparta] 내일배움캠프 TIL 33일차 (0) | 2024.06.17 |
[Sparta] 내일배움캠프 TIL 32일차 (0) | 2024.06.14 |
[Sparta] 내일배움캠프 TIL 31일차 (0) | 2024.06.13 |