TDD(Test Driven Development)

    TDD

    TDD란 Test Driven Development의 약자로 테스트 주도 개발을 의미한다. 요구상항은 계속 변하는데, 서비스의 품질을 항상 안정적으로 유지해야 하기 때문에 TDD를 해야 한다.

    • 요구사항 변경

      • 새로운 기능 추가
      • 기존 기능 변경
      • 기능은 그대로지만 기술적 변경
      • 기능은 그대로지만 디자인 변경
      • 이외에도 개발팀에서 대응해야 하는 모든 변경사항
      • -
    • TDD 사이클

      1. 빨강 - 실패하는 작은 테스트를 빨리 작성한다. 처음에는 컴파일조차 되지 않을 수 있다.
      2. 초록 - 빨강 테스트가 통과하게끔 간단하게 만든다. 이를 위해 어떤 죄악을 저질러도 좋다.
      3. 리팩토링 - 일단 테스트를 통과하게만 하는 와중에 생겨난 모든 중복을 제거한다.

    TDD의 궁극적인 목적은 작동하는 깔끔한 코드를 얻는 것이다. 한번에 이루기는 어려운 목표이므로 나누어서 정복하자(divide and conquer). 일단 ‘작동하는’에 해당하는 문제를 먼저 해결하고, ‘깔끔한 코드’ 부분을 해결한다. 일단 동작하는 쓰레기라도 만들고, 이후 계속해서 리팩터링하여 깔끔한 코드로 거듭날 수 있게 하는 기반을 만드는 것이 TDD다.

    리팩터링이란

    리팩터링이란 기능을 구대로 유지한 채, 내부 구현만 개선하는 것이다. 그런데 리팩터링을 했을 때, 기능이 잘 동작한다는 것을 어떻게 보장할까? TDD를 통해 가능하다. TDD는 프로그래밍을 하면서 나타나는 두려움을 관리하는 방법이다.

    테스트 종류

    • E2E 테스트

      • slower, expensive
    • Unit 테스트

      • faster, cheaper

    웹 프론트엔드에서 주로 하는 일은 다음과 같다.

    • 네트워크

      • 애플리케이션 서버 외부 API
    • 상태

      • 도메인 데지터 비즈니스 로직
    • UI

      • 브라우저 렌더링, 사용자 인터랙션

    E2E 테스트는 네트워크, 상태, UI까지 왔다갔다하며 전체를 다 테스트한다. UNIT 테스트는 상태 등 더 작은 단위인 개별 영역에 대해서만 테스트한다. 즉, E2E 테스트와 UNIT 테스트의 차이는 테스트 대상과 범위가 달라지는 데 있다.

    E2E 테스트는 실제 앱의 동작을 그대로 테스트할 수 있지만, 그렇기 때문에 다음과 같은 단점이 있다.

    • 피드백 주기가 긴 편이다. 하나의 케이스를 통과하기 위해 많은 것을 구현해야 한다.
    • 테스트가 실패하는 이유가 여러가지일 수 있다.
    • 테스트 작성 비용도 크다.
    • 랜덤 등 제어할 수 없는 대상에 대해서는 테스트가 어렵다.

    Given / When / Then 패턴

    • Given (주어진 환경) : 특정 값이 주어지고(Given)

      • 유저에게 계산기 화면이 렌더링 된 후
    • When (행위) : 어떤 이벤트가 발생했을 때(When)

      • 유저가 숫자 4을 클릭한다.
      • 유저가 + 버튼을 클릭한다
      • 유저가 2를 클릭한다.
      • 유저가 = 을 클릭한다.
    • Then (기대결과) : 그에 대한 결과를 보장해야 한다(Then).

      • 계산기 화면에는 6이라는 숫자가 보여진다.

    TDD에 대한 생각

    우테코에서 들은 TDD에 대한 여러 생각들이다. 고민해보자.

    • 무엇을 테스트하지 않을지를 결정해야 한다. 불필요한 곳에서는 테스트하지 않고 핵심적인 곳에만 테스트를 유지해도 좋다.
    • 테스트하기 어려운 코드들이 섞여 있으면 단위테스트하기 어렵다. 따라서 테스트하기 어려운 코드를 분리하여 개발하는 역량을 키워야 한다. 테스트하기 어려운 코드는 테스트하지 않는다.
    • 현업에서는 서비스 성격에 따라, E2E테스트와 유닛테스트를 동시에 사용하는 등 중요도가 달라진다. E2E는 작성 비용도 크고 실행 속도도 상대적으로 느리다보니, 가장 서비스에서 중요한 사용 플로우(ex, 결제시스템)에 제한적으로 사용하는 편이다.
    • 시간이 지나서 레거시가 쌓일수록 테스트의 중요성이 커진다.
    • 처음부터 설계를 완벽히 할 수 없다. 설계는 끊임없이 계속 해야 한다. 작은 단위의 설계 변경을 게속해야 한다. 그럴려면 테스트코드가 바탕이 되어야 한다. TDD는 리듬을 타면 재밌다.
    • 테스트하기 쉬운 코드를 만들다보면, 조금 더 유연한 코드(유지보수하기 좋은 코드)를 만들 가능성이 높아진다.
    • 테스트 코드도 ‘코드’다. 유지보수의 대상이며, 작성하는 비용 대비 효용을 생각할 필요도 있다.

    Written by@Marco

    GitHub