통합 테스트와 단위 테스트
F.I.R.S.T 원칙
Given-When-Then 패턴
JUnit + Mockito
@Mock: Mock 객체를 만들어 반환해주는 어노테이션
@Spy: Stub하지 않은 메소드들은 원본 메소드 그대로 사용하는 어노테이션
@InjectMocks: @Mock 또는 @Spy로 생성된 가짜 객체를 자동으로 주입시켜주는 어노테이션
예를 들어 UserController에 대한 단위 테스트를 작성하고자 할 때, UserService를 사용하고 있다면 @Mock 어노테이션을 통해 가짜 UserService를 만들고, @InjectMocks를 통해 UserController에 이를 주입시킬 수 있음.
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Spy
private JwtUtil jwtUtil;
@Spy
private BCryptPasswordEncoder passwordEncoder;
@Test
@DisplayName("회원 가입")
void signup() {
// given
SignupRequest request = SignupRequest.builder()
.admin(false)
.username("nathan")
.password("1234qwer")
.email("nathan@gmail.com")
.build();
when(userRepository.findByUsername(any(String.class)))
.thenReturn(Optional.empty());
// when
ResponseStatusDto response = userService.signup(request);
// then
assertThat(response.getStatusCode()).isEqualTo(StatusEnum.SIGN_SUCCESS.getStatusCode());
assertThat(response.getMsg()).isEqualTo(StatusEnum.SIGN_SUCCESS.getMsg());
}
@Test
@DisplayName("로그인")
void login() {
// given
LoginRequest request = LoginRequest.builder()
.username("nathan")
.password("1234qwer")
.build();
User user = new User("nathan", passwordEncoder.encode("1234qwer"), null, null);
when(userRepository.findByUsername(any(String.class)))
.thenReturn(Optional.of(user));
MockHttpServletResponse servletResponse = new MockHttpServletResponse();
// when
ResponseStatusDto response = userService.login(request, servletResponse);
// then
assertThat(response.getStatusCode()).isEqualTo(StatusEnum.LOGIN_SUCCESS.getStatusCode());
assertThat(response.getMsg()).isEqualTo(StatusEnum.LOGIN_SUCCESS.getMsg());
assertThat(servletResponse.getHeaderValue("Authorization").toString()).isNotEmpty();
verify(userRepository).saveAndFlush(any(User.class));
}
}
@ExtendWith(MockitoExtension.class)
class PostServiceTest {
@Mock
private PostRepository postRepository;
@InjectMocks
private PostService postService;
@Test
void createPost() {
// given
PostRequestDto request = new PostRequestDto();
User user = new User();
// when
postService.createPost(request, user);
// then
verify(postRepository).save(any(Post.class));
}
@Test
void getPosts() {
// given
Pageable pageable = Pageable.ofSize(20);
when(postRepository.findAll(pageable)).thenReturn(Page.empty());
// when
Page<PostResponseDto> response = postService.getPosts(pageable);
// then
assertThat(response).isEmpty();
// verify
verify(postRepository).findAll(pageable);
}
@Test
@DisplayName("게시글 id가 없을때 예외 발생")
void getPostById_throw() {
// given
when(postRepository.findById(anyLong())).thenReturn(Optional.empty());
// when & then
assertThrows(IllegalArgumentException.class, () -> {
postService.getPostById(0L);
});
}
@Test
@DisplayName("게시글 가져오기")
void getPostById() {
// given
PostRequestDto request = PostRequestDto.builder()
.contents("hello.. hello..")
.title("hello")
.build();
Post post = new Post(request, new User());
when(postRepository.findById(anyLong())).thenReturn(Optional.of(post));
// when
PostResponseDto response = postService.getPostById(anyLong());
// then
assertThat(response.getContents()).isEqualTo(request.getContents());
assertThat(response.getTitle()).isEqualTo(request.getTitle());
// verify
verify(postRepository, times(1)).findById(anyLong());
}
@Test
@DisplayName("게시글 업데이트")
void updatePost() {
// given
PostRequestDto request = PostRequestDto.builder()
.contents("hello.. hello..")
.title("hello")
.build();
User user = new User("nathan", "1234qwer", null, null);
Post post = new Post(request, user);
when(postRepository.findById(anyLong())).thenReturn(Optional.of(post));
// when
postService.updatePost(anyLong(), request, user);
// verify
verify(postRepository, times(1)).save(any(Post.class));
}
@Test
@DisplayName("게시글 업데이트 (다른 사람이 작성하려할때)")
void updatePost_invalid_user() {
// given
PostRequestDto request = PostRequestDto.builder()
.contents("hello.. hello..")
.title("hello")
.build();
User userInPost = new User("nathan", "1234qwer", null, null);
User userInput = new User("joel", "1234qwer", null, null);
Post post = new Post(request, userInPost);
when(postRepository.findById(anyLong())).thenReturn(Optional.of(post));
// when & then
assertThrows(IllegalArgumentException.class, () -> postService.updatePost(anyLong(), request, userInput));
}
}
Generics (제네릭스)
public class _01_Generics {
public static void main(String[] args) {
// 제네릭스
// 정수형 배열
Integer[] iArray = {1,2,3,4,5};
Double[] dArray = {1.0, 2.0, 3.0, 4.0, 5.0};
String[] sArray = {"A", "B", "C", "D", "E"};
printIntArray(iArray);
printDoubleArray(dArray);
printStringArray(sArray);
System.out.println("-------------------");
printAnyArray(iArray);
printAnyArray(dArray);
printAnyArray(sArray);
}
// T : type 이라는 뜻, K :Key V: Value E:Element
private static <T> void printAnyArray(T[] array){
for(T t : array){
System.out.print(t + " ");
}
System.out.println();
}
private static void printStringArray(String[] sArray) {
for(String s : sArray){
System.out.print(s + " ");
}
System.out.println();
}
private static void printDoubleArray(Double[] dArray) {
for(double d : dArray){
System.out.print(d + " ");
}
System.out.println();
}
private static void printIntArray(Integer[] iArray) {
for(int i : iArray){
System.out.print(i + " "); // 1 2 3 4 5
}
System.out.println();
}
}