테스트 코드, 후발대 수업

우정·2023년 1월 26일
0

[내일배움캠프] TIL

목록 보기
42/50

테스트 코드

통합 테스트와 단위 테스트

  • 단위 테스트 : 가장 작은 단위의 테스트 방식. 메서드 단위로 테스트를 수행하고, 의도한 결과값이 나오는지 확인하는 수준 (ex: controller, service, repository… 격리해서 따로따로)
  • 통합 테스트 : 어플리케이션이 정상적으로 동작하는지 확인. 여러모듈을 함께 테스트 (ex: controller 메소드를 호출하면 controller → service→ repository 모두 수행)

F.I.R.S.T 원칙

  • Fast : 단위 테스트는 빨라야 함
  • Independent : 단위 테스트는 각각의 테스트가 연관되지 않고 고립되어야 함. 즉 독립적 이어야 함
  • Repeatable : 단위테스트는 반복 가능해야함. 반복적으로 테스트를 시행해도, 결과는 같아야 함.
  • Self-validating : 자체적으로 테스트 결과가 도출되어야함. (개발자가 print 찍어서 비교하는게 아니고 자동으로 수행되며 성공/실패 의 결과가 나오는 것)
  • Timely(적시성) : 단위 테스트는 실제 코드보다 먼저 구현해야함. (TDD일 경우에만 해당)

Given-When-Then 패턴

  • Given : 테스트에 필요한 변수를 정의하거나 Mock 객체를 통해 특정 상황에 대한 행동을 정의함.
  • When: 실제 테스트를 하는 메소드가 호출되며 테스트를 통한 결과값을 가져옴.
  • Then: When 단계에서 나온 결과값을 검증하는 작업을 수행함.

JUnit + Mockito

  • JUnit : Java 에서 사용되는 대표적인 테스트 프레임워크. 자바 개발자 93% 가 사용한다는 통계가 있음.
  • Mockito : Mock 객체를 쉽게 만들고, 관리하고, 검증할 수 있는 방법을 제공하는 프레임워크. Mock 객체는 진짜 객체와 비슷하게 동작하지만 프로그래머가 직접 행동을 관리하는 객체임.
    💡 Mockito에서 Mock(가짜) 객체의 의존성 주입을 위해서는 크게 3가지 어노테이션이 사용됨.
    • @Mock: Mock 객체를 만들어 반환해주는 어노테이션

    • @Spy: Stub하지 않은 메소드들은 원본 메소드 그대로 사용하는 어노테이션

    • @InjectMocks: @Mock 또는 @Spy로 생성된 가짜 객체를 자동으로 주입시켜주는 어노테이션

      예를 들어 UserController에 대한 단위 테스트를 작성하고자 할 때, UserService를 사용하고 있다면 @Mock 어노테이션을 통해 가짜 UserService를 만들고, @InjectMocks를 통해 UserController에 이를 주입시킬 수 있음.

  • UserServiceTest
@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));
    }
}
  • PostServiceTest
@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 (제네릭스)

  • 다양한 타입의 객체를 지향하는 클래스나 인터페이스,또는 메서드를 정의하는 방법
  • 똑같은 동작을 여러번 만들 필요 없이 딱 한 번만 정의하고나서 여러 타입에 대해서 지원을 해주도록 하는것
  • _01_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();
        }
    
    }

0개의 댓글

관련 채용 정보