우리의 코드가 테스트하기 어려운 이유

  • 테스트를 작성해야하는 이유는 모두가 알고있음
  • 알고있지만 작성하지않음
  • 막상 짜려고하면 너무 어려움
  • 테스트를 작성하는건 그 자체로 지루한 일
  • 시간을 핑계대지만 시간이 많으면 정말 테스트를 짤까
  • 우리가 보통 작성하는 코드에서 테스트를 짜기 힘든 이유를 고민해보기위함
  • 너무 적나라한 얘기를 하게될까봐 두려움

목적

Service layer

  • 객체는 레이어를 나누거나 비즈니스 로직을 담거나 한가지만 해야함
  • @Controller @Service @Repository 는 대표적인 레이어를 나누는 애노테이션들
  • 레이어를 나눠야하는 객체에 비즈니스 로직을 담는 순간 테스트 난이도는 급상승

Service layer

  • 객체지향언어는 객체들이 서로 협력하며 문제를 해결하게 함
  • service layer 는 이 객체들이 협력하는 협력의 장을 제공
  • 분리만 잘 한다면 service 에는 테스트된 객체들을 호출하기만 함으로써 service 자체엔 테스트가 필요없는 상황이 오기도 함
  • 로직이 service 에 담기면 테스트된 객체들을 호출하는게 아니라 service 그 자체를 테스트해야하는 상황이 발생

Service layer

public class ExampleServiceTest {
    @MockBean
    private NestedService nestedService;

    @Autowired
    private ExampleService exampleService;

    @Test
    void test() {
        given(nestedService.doSomething()).willReturn("hello test");
    }
}

Service layer

  • service 는 협력의 장이기때문에 다른 service, repository 등에 의존함
  • service 에 대한 테스트를 작성하려면 이런 의존성에 대한 mocking 부터 진행해야함
  • 인프라스트럭처에 대한 mocking 을 하고나면 일단 1차적으로 진빠짐
  • 그래도 최선을 다해 겨우겨우 다 짜고나면 솔직히 뭘 테스트하는건지 알아보기 힘든 경우가 대다수
  • 실제 테스트는 몇라인 안되고 mocking 이 테스트 코드 절반 이상 차지하는 현상 발생

Service layer

  • 차후에 이런 테스트코드를 만나게되면 조심조심 테스트를 돌려봄
  • 그나마 green 이 뜨면 다행이고 red 가 뜨면 수정할 엄두도 못냄
  • red 인 테스트는 없느니 못한 테스트가 되었지만 기존재 코드라서 함부로 지우지도못하고 그렇게 쌓여감

Service layer

  • 물론 service layer 에 대한 통합테스트도 중요함
  • 테스트의 비율을 잘 조절해야하는데 기본적인 비즈니스 로직 테스트는 도메인 객체들의 단위테스트에서 정리해야함

Mock

  • mockito, mockk 같은 mock 라이브러리들은 테스트를 간편하게 만들어줌
  • 실제 클래스 디자인에 문제가 있는 경우에도 조작을 해버리기때문에 점점 mock 에만 의존하게 만듦
  • 테스트를 하기도전에 협력 객체들에 대해 "가정"을 해야하기때문에 내부구현에 대한 테스트를 하도록 유도하며 테스트 코드의 가독성을 떨어뜨림
  • 외부 프로세스에 대한 의존(DB, kafka 등) 외에는 가급적 사용하지 않으려는 노력이 필요
  • 객체가 필요하면 직접 생성

Assertion

  • 구현이 아닌 스펙을 테스트해야함
  • return 을 하는 메서드라면 return 되는 값에 대한 assertion
  • void 메서드라면 부수효과에 대한 assertion
  • 그 외에 대한 가정은 구현에 대한 테스트일 확률이 높음
  • verify 같은 assertion 은 가급적 자제해야함
  • mock 라이브러리들은 mocking 한 메서드에 대해 호출이 발생하지않으면 예외를 불러일으킴
  • 이 자체로 구현에 대한 테스트를 만들게함
  • 실제로 객체의 메서드 내부에서 어떤 메서드를 호출하고 어떻게 협력하는지는 철저하게 캡슐화되어야 할 부분

private

  • 리플렉션을 이용
  • 접근제어자 변경
  • 안함
  • 의외로 안하는게 옳음

private

  • 하지만 public 메서드 내에서 다양한 private 메서드를 호출하는경우 public 을 한번 호출하면 내부에 mocking 해야할 private 메서드들이 많고, 테스트를 작성하기 어려운 경우가 있음
  • 이는 해당 객체에 추상화가 부족하다는 증거
  • 내부 역할에 대한 추가적인 추상화가 필요하며 private 메서드들을 별도 객체로 분리한다면 private 메서드는 분리된 클래스에서 public 메서드가 됨

마무리

  • 그냥 테스트짜다가 힘들어서 급하게 발표 주제를 만들어 봄
  • 모두 화이팅

당신의 코드가 테스트하기 어려운 이유

By changyong

당신의 코드가 테스트하기 어려운 이유

  • 182