Frontend

단위테스트 책 후기

mechaniccoder 2023. 6. 8. 23:11

책 후기

유튜브 영상을 통해 단위테스트라는 책을 추천받아서 읽었는데 느낀점과 정리한 내용을 간략히 공유해보겠습니다.

느낀점

integration testing vs unit testing

과거에 프런트엔드 테스팅(엄밀히 말하자면 redux testing)에 관한 아티클을 읽었는데 integration testing으로 점점 더 옮겨가는 추세라는 내용의 article이었습니다. 하지만 지금 생각으로는 복잡하지 않은 어플리케이션에서는 integration testing으로 충분히 가능하지만 복잡한 SaaS 프러덕트의 경우는 integration testing으로는 힘들다고 결론을 내렸습니다. 복잡한 비지니스 로직을 integration testing으로 커버하기에는 한계점이 있습니다. integration testing은 느리며 제어할 수 없는 의존성을 mocking해야하기 때문이죠. 물론 최근 프런트엔드 테스팅에 testing library와 msw를 활용하여 이를 극복할 수는 있겠지만 로직이 복잡할수록 순수함수로 분리하여 단위테스트로 테스트를 하는 것이 유지보수하기도 쉬우며 더 빠른 피드백을 가져갈 수 있다는 장점이 있는 것 같습니다.

테스트하기 쉬운 코드

책에서 이 부분을 설명할때 최근 공부한 함수형 프로그래밍에 관련한 내용이 나와서 더 좋았던 것 같습니다. 한마디로 요약하자면 테스트하기 어려운 사이드 이펙트를 비지니스 로직으로부터 분리하여 테스트하기 쉬운 순수함수로 만든다라는 내용입니다. 이 부분에 대해서는 인프런 CTO이신 향로님의 강의도 있습니다. (개발자 원칙)

내용 정리

3장 - 단위테스트 구조

  • e2e 테스트는 통합테스트의 한 종류이며 통합테스트보다 더 많은 외부 의존성을 가진다. 통합테스트에서 보통 제어 가능한 의존성을 테스트 대역으로 교체한다.
  • 테스트간의 공유되는 fixture는 안티패턴이다. 테스트간의 결합이 되기 때문이다. 그것보다는 팩토리 메서드를 활용하는 방법이 있다
  • 코드를 테스트하지 말고 동작을 테스트해라. sut를 이름에 포함시키지 마라.
  • should be 보다는 is를 사용해 사실을 서술하자.
  • parameterized test를 활용해 비슷한 동작을 그룹화하자. jest에서는each`라는 api를 제공하고 있다.

4장 - 좋은 단위테스트의 4대 요소

회귀방지

  • 회귀 방지를 위해 가능한 테스트가 많은 코드를 실행해야한다.
  • 중요한 부분을 테스트한다.

리팩토링 내성

  • 리팩토링을 해도 테스트는 통과할 것에 대한 척도이다.
  • 거짓 양성 (기능은 잘 동작하지만 테스트는 실패하는 것)은 신뢰할 수 없는 테스트를 만든다. -> 신뢰가 부족하면 리팩토링하는데 주주저한다.
  • 인터페이스가 아닌 구현을 테스트하면 거짓양성이 나올 가능성이 크다.

빠른 피드백

유지보수성

  • 리팩토링 내성은 있거나 없거나 이므로 무조건 있는 방향을 선택할 수 밖에 없다. 그러면 빠른 피드백과 회귀방지 그 사이에서의 트레이드 오프가 필요하다.

5장 - Mock과 테스트 취약성

  • 테스트 대역은 여러가지 종류가 있지만 크게 mock(mock, spy)과 stub(dummy, stub, fake)으로 나눌 수 있다.
  • 목은 외부로 나가는 의존성, stub은 내부로 들어오는 의존성에 사용한다.
  • spy는 수동으로 작성하는 반면, mock은 프레임워크의 도움을 받는다.
  • stube은 sut가 최종결과를 생성하도록 입력을 제공한다. msw에서 GET 메서드를 mocking하는 것은 mock이 아닌 stub으로 볼 수 있고 그로 인해 그려지는 component의 결과를 테스트한다고 볼 수 있다.
  • 어플리케이션과 도메인 계층이 있는 헥사고날 아키텍처
  • 어플리케이션에는 시스템내, 시스템간의 통신이 있고 시스템간의 통신만 목으로 처리한다.

6장 - 단위테스트 스타일

  • 출력 > 상태 > 통신
  • 출력 기반 단위테스트 -> 함수형 프로그래밍에 뿌리를 둔다. 순수함수를 사용하기 때문 (도메인, 비지니스 로직에 해당)
  • 상태 기반 단위테스트 -> 실행이 완료된 후 sut의 상태를 확인한다. (프런트엔드에서 컴포넌트 테스팅이 이에 해당된다.)
  • 통신 기반 단위테스트 -> sut와 협력하는 것을 테스트한다.
  • 함수형 아키텍처의 근본은 비지니스 로직을 처리하는 부분과 사이드 이펙트를 일으키는 코드를 분리하는 것이다. 리액트를 생각해보면 렌더링을 담당하는 코드와(비지니스 로직은 아니지만) 사이드 이펙트를 담당하는 코드 (useEffect 등...)이 분리되어 있다.
  • 함수형 아키텍처도 단점은 있다. 성능적인 부분인다. 도메인 로직이 실행되는 과정에서 통신을 해야한다면 그 이전에 통신을 미리 하거나 일관된 인터페이스를 위해 공통으로 통신을 하는 방법을 취해야 하는데 이로인해 성능이 저하된다. 이럴 경우에는 절차적으로 처리하는 방법이 나을 수도 있다.
  • 함수형 아키텍처는 코드베이스의 증가로 이어지는 경향이 있다. 함수형 코어(비지니스 로직)와 가변 셀을 나누기 때문이다.
  • 순수하게 빼내는데 너무 많은 비용이 든다면 사용하지 않는 것이 방법일 수 있다. 역시 모든건 다 trade off이다.

7장 - 가치 있는 단위테스트를 위한 리팩터링

  • 도메인과 연관성이 있으며 복잡도가 높은 코드가 테스트하기 가장 좋다.
  • "좋지 않은 테스트를 작성하는 것보다 테스트를 전혀 작성하지 않는 것이 낫다."
  • 험블객체 - 의존성을 격리시켜 테스트하기 쉬운 코드를 분리해낸다.