ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 테스트팁 - 테스트 작성 방법
    TDD 2024. 6. 26. 20:16
    test

     

     

    테스트를 작성안하면 결국에는 리팩토링하다가 뭔가를 깨트릴 것이다. 그러면 여러분은 리팩토링에 대해 안 좋은 느낌을 갖게 되고, 리팩토링을 덜 하게 된다. 리팩토링을 더 적게하면 설계의 질이 저하되고, 결국에는 여러분은 해고될 것이다. 여러분의 강아지도 곁을 떠날 것이고, 여러분은 자신의 영양 상태에 신경을 쓰지 못하게 될것이다, 그러면 치아도 나빠진다, 자 치아를 건강하게 유지하기위해 리팩토링하기 전에 테스팅을 하자. 
    - 켄트 백 -

     💡 이 게시글은 계속해서 변화하는 게시글입니다.
    제가 테스트 코드를 작성하면서 느낀 것을 나열한 것으로 언제든지 방향을 바꿀수도 있습니다.

    테스트 코드는 쉬운 테스트부터 작성한다

    쉬운 테스트는 예외와 실패 테스트를 의미하고, 어려운 테스트는 성공 테스트를 의미한다.

    성공 테스트는 복잡한 구조를 테스트해야 하기 때문에 피드백이 느려진다. 또한 성공 테스트를 먼저 만들고 실패 테스트를 작성하면 이미 만들어진 로직에 실패 분기를 추가해야 한다.

    테스트 작성이 용이한 코드는 좋은 코드이며, 피드백이 빠른 테스트 코드는 좋은 테스트 코드다.

     💡 조심해야 하는 건 테스트하려는 대상의 테스트가 맞는지 조심해야 한다.
    예로 controller 테스트 시 인증 정보가 없는 유저의 요청을 테스트 대상이라고 생각할 수 있지만 이는 스프링 스큐리티의 테스트이다.

    대상 클래스 파일 하나당 테스트 클래스 파일 하나로 구성하자

    서비스 클래스에 public 메서드가 2개 있을 때 2개의 메서드에 대한 성공, 실패 테스트를 하나의 클래스 파일로 처리할지 두 개로 처리할지 고민된다.

    하나로 처리하면 public 메서드마다 성공 실패 테스트가 들어가니 파일의 크기가 커진다.

    두 개로 처리할 경우 테스트 클래스 이름과 대상 클래스 이름의 매핑이 어렵다.

    많은 프로젝트를 보면 테스트 클래스를 하나로 처리하고 커지는 것은 신경 쓰지 않는다. 따라가자.

    Final Class는 Mockito 를 사용할수없으니 MockMaker 를 사용하자

    Final Class 는 Mock 할 수 없다. Final을 제거하는 것보단 Mocker를 통해 바이트 조작으로 Mocking 하도록 한다.

    vi /test/respources/mockito-extensions/org.mockito.plugins.MockMaker
    

    test 디렉터리에 위의 경로와 파일을 추가하고

    그 안에 mock-maker-inline 내용만 추가한다.

    given, when, then 방식을 사용한다면 BDDMockito 사용하자

    void test() {
        //given
        String loginId = "";
        Mockito.when(memberRepository.findById(loginId))
                .thenReturn(Optional.of(new Member()));
        //when
        Token token = memberService.login(loginId);
        
        //then
        Assertions.assertThat(token).isNotNull();
        Mockito.verify(memberRepository,Mockito.times(1)).findById(loginId);
    }

    given, when, then 구조로 테스트를 진행할 때 mock을 하는 과정을 보면

    given 영역인데 메서드 명은 Mockito.when() 이게 부적절해 보일 수 있다.

    이때 Mockito를 상속한 BDDMockito를 사용하면 given, when, then 스타일로 읽기 좋아진다.

     

    void test() {
        //given
        String loginId = "";
    
        BDDMockito.given(memberRepository.findById(loginId))
                .willReturn(Optional.of(new Member()));
        //when
        Token token = memberService.login(loginId);
        //then
        Assertions.assertThat(token).isNotNull();
        BDDMockito.then(memberRepository).should().findById(loginId);
    }

    특정 환경에서 테스트를 실행시킬 수 없다면 가능한 것이라도 동작시키자

    모든 환경에서 가능한 테스트가 가장 바람직 하지만

    그렇지 못한 경우를 만드는 이유가 다양하게 존재한다.

    다른 곳에서 테스트를 실행할 수없다고 모든 곳에서 테스트를 하지 않는 것보단

    가능한 곳에 서라도 테스트를 처리해야 한다.

     

    build.gradle 에서

    test {
        exclude("com/**/repository/*.java")
    }
    

    exclude를 사용하여 특정 디렉터리 혹은 파일을 지정하여 테스트에서 제외시킬 수 있다.

    그러나 보통 저렇게 한 곳에 모여있는 게 아니기에 Junit의 Tag를 사용하여 제외시킬 수 있다.

    @Tag("LocalOnlyTest")
    public abstract class LocalOnlyTest {
    }
    

    위처럼 local에서만 동작한다는 의미의 클래스를 생성하고

    build.gradle 에서 해당 Tag가 달린 모든 클래스를 제외시킨다.

    test {
        def localTest = System.getProperty('localTest');
    
        if (localTest == null) {
            useJUnitPlatform {
                excludeTags 'LocalOnlyTest'
            }
        }
    }
    

    이제 실행시킬 때 변수로 localTest 넘겨주면 테스트를 진행하고

    넘기지 않으면 해당 태그가 달린 테스트가 동작하지 않는다.

    ./gradlew test -DlocalTest
    

     

    'TDD' 카테고리의 다른 글

    테스트팁 - 테스트 하기 좋은 코드  (0) 2024.06.26
    테스트팁 - 좋은 테스트 코드  (0) 2024.06.26