[스프링]단위 테스트 코드(1/2)

allnight5·2023년 1월 26일
0

스프링

목록 보기
38/62

통합 테스트와 단위 테스트

  • 통합 테스트 : 어플리케이션이 정상적으로 동작하는지 확인. 여러모듈을 함께 테스트 (ex: controller 메소드를 호출하면 controller → service→ repository 모두 수행)
  • 단위 테스트 : 가장 작은 단위의 테스트 방식. 메서드 단위로 테스트를 수행하고, 의도한 결과값이 나오는지 확인하는 수준 (ex: 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에 이를 주입시킬 수 있습니다. 

assertThat 와 verify

assertThat().isEqualTo를 통한 결과값비교를 통한 결과값 검증
verify에서 times를 이용하여 몇번 호출되었는지를 확인하여 실행이 되어쓴지 확인하는 행위 검증

인텔리J에서 테스트 코드 생성방법
클래스명에 우클릭후 Generate를 선택 -> Test선택
Testing library는 JUnit 으로 되어있을것이고 여기서이제 @Mock을 사용하니
JUnit+ Mock이라는 것이다.

@ExtendWith(MockitoExtension.class)
//JUnit과 Mock를 연결해주는 어노테이션

@Mock
//가짜 객체를 만들겠다.
private UserRepository userRepository

@InjectMocks
//이 서비스를 테스트하겠다.
//@Mock 또는 @Spy로 생성된 가짜 객체를 자동으로 주입시켜주는 어노테이션
private UserService userService;

@Spy// Stub하지 않은 메소드들은 원본 메소드 그대로 사용하는 어노테이션
private JwtUtil jwtutil;
@Spy
private BCryptPasswordEncoder passwordEncoder;

@Test
@DsiplayName("회원가입")//이 것의 한눈에 알아볼수있는 내용을 적어주면된다.
void singup(){
	//given
    SignupRequest request = SignupRequest.builder()
    						.admin(false).username("what").password("test1")
                            .email("2nd@naver.com").build();
                          
    when(userRepository.findByUsername(any(String.class)))
    		.theReturn(Optional.empty());
            //any(String.class) 모든 문자를 받겠다.
            //.theReturn(Optional.empty())이 란에서 중복검사해서 없을때 넘어가는거라
            //Optional.empty()를 해준것으로 다른것이라면 그에 맞는 리턴값을 적어줘야한다.
            //그런것도 있고 Repository에서 값을 가져오기 때문에 여기에 무엇이 들어갈것인가를 넣어주는것이다.
            //7이 나와야 넘어가야한다면 7을 넣어주면된다.
    //조건문이 없다면 when을 해주지 않고 실행하는 척만하면서 넘어가기때문에 if문이 없다면
    //when해주지 않아도 된다.
    
    //when
    ResponseStatusDto response = uerService.signup(request);
    //then
    assertThat(response.getStatusCode()).isEqualTo(StatusEnum.SIGN_SUCCESS.getStatusCode());
    assertThat(response.getMsg()).isEqualTo(StatusEnum.SIGN_SUCCESS.getMsg());
    //return값이 String 문자열 두개(StatusCode, Msg)를 반환해주는 Dto에
    //StatusEnum.SIGN_SUCCESS.getStatusCode(),StatusEnum.SIGN_SUCCESS.getMsg()
    //저 두개가 문자열 이기때문에 isEqualTo로 같은지 확인하는것이다.
    
    } 
    @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();
        //HttpServletResponse는 웹에서 컨트롤러에서 받아 service로 넘겨주는데
        //이것을 만들어 주는부분이 MockHttpServletResponse라는 참조형이다.
        //MockHttpServletRequeste도 있다.

        // 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();

//assertThat 결과값 검증 verify행위 검증
        verify(userRepository, times(1)).saveAndFlush(any(User.class));
        //verify 가짜객체에 있는 함수가 (실제함수가 호출되지는 않지만) 비지니스로직상
        // 이 함수가 호출이 되었는지 확인하기 위해서 사용한다.
        //userRepository.saveAndFluse(user);
        //times 1번이 호출 되지 않았다면 실패 
        //리턴값이 없을때 verify를 통해서 진짜 실행됬는지 검증할수있다.
    }
}
    @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());
        //선택한 id를 불러오는 부분에 대신 넣어주기위해서 위에서
        //post를 생성하여 만들어줌
        
        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("게시글 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());
    }
profile
공부기록하기

0개의 댓글