본문 바로가기
공부 기록

[클린코드] 9장. 단위 테스트

by 매트(Mat) 2024. 4. 23.

9장. 단위 테스트

단위 테스트란 자기 프로그램이 '돌아간다'는 사실만 확인하는 일회성 코드에 불과했다. (90년대까지만 해도..)

현재는 단위 테스트를 자동화하는 경우까지 생기면서 단위 테스트의 중요성이 점점 커지는 추세다. 하지만 단순히 테스트 케이스를 작성한다고 되는 것이 아니라 제대로 작성하는 것이 중요하다.

TDD 법칙 세 가지

TDD는 실제 코드를 작성하기 전에 테스트부터 짜는 것을 말하는데, 정확히는 아래 세 가지 법칙이 있다.

  1. 실패하는 단위테스트를 작성할 때까지 실제 코드를 작성하지 않는다.
  2. 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위테스트를 작성한다.
  3. 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.

이렇게 일하면 매일 수십, 수백, 수천 개에 달하는 테스트 케이스가 나오며, 실제 코드를 사실상 전부 테스트하게 된다. 하지만 테스트 케이스가 많아지는 만큼 심각한 관리 문제를 유발할 수 있다.

깨끗한 테스트 코드 유지하기

테스트를 작성하기 전에는 기능을 그저 돌아가게만 만들고, 실제 실행해서 수동 테스트만 하면 그만이었다.

실제 코드에 대한 테스트 케이스가 많아지면서 실제 코드가 변경되면 테스트 코드도 같이 변경해야 하기 때문에 부담이 된다. 그렇다고 테스트 코드를 작성하지 않으면 매번 수동 테스트를 해야하는건 더 시간이 오래걸린다.

  • 테스트 코드를 엉망으로 짜더라도 짜는 것이 좋다?
  • 그렇지 않다. 엉망으로 짤거면 테스트 코드를 작성하지 않는 것이 좋다.
  • 하지만 테스트 코드는 반드시 작성해야 한다.
  • 따라서 테스트 코드를 실제 코드 못지 않게 잘 작성하는 것이 중요하다.
  • 테스트 코드 역시 사고와 설계와 주의가 필요하다.

테스트는 유연성, 유지보수성, 재사용성을 제공한다.

테스트 케이스가 있으면 변경이 두렵지 않다. 반대로 테스트 케이스가 없으면 모든 변경이 잠정적인 버그가 된다. (아키텍처가 아무리 유연하더라도.)

따라서 테스트 커버리지가 높을수록 변경에 대한 두려움이 줄어든다. 그래서 테스트 코드를 실제 코드만큼 잘 작성해야 한다는 것이다.

깨끗한 테스트 코드

깨끗한 테스트 코드를 작성하기 위해서는 다음 세 가지가 필요하다.

  • 가독성
  • 가독성
  • 가독성

가독성은 실제 코드보다 테스트 코드에 더더욱 중요하다.

테스트 코드를 작성할 때 BUILD-OPERATE-CHECK 패턴이 테스트 구조에 적합하다. 각 테스트는 명확히 세 부분으로 나눠진다.

  1. 테스트 자료를 만든다. (given)
  2. 테스트 자료를 조작한다. (when)
  3. 조작한 결과가 올바른지 확인한다. (then)

흔히 우리가 테스트 코드를 작성할 때는 쓰는 given-when-then 패턴이다. 이러한 패턴이 있으므로 코드가 복잡하지 않고 명확해진다.

도메인에 특화된 언어(DSL)로 테스트 코드를 구현하는 경우

흔히 쓰는 시스템 조작 API를 사용하는 대신 API 위에다 함수와 유틸리티를 구현한 후 그 함수와 유틸리티를 사용하므로 테스트 코드를 짜기도 읽기도 쉬워진다. 즉, 테스트를 구현하는 당사자와 나중에 테스트를 읽어볼 독자를 도와주는 테스트 언어다.

이중 표현

실제 코드

String str = "";
str += "H";
str += "B";
str += "C";
str += "L";
...

실제 코드에서 위처럼 String에 계속 + 연산은 성능에 좋지 않다. 하지만 테스트 환경에서는 이러한 자원에 제한적일 가능성이 낮다. 이것이 이중 표준의 본질이며, 실제 환경에서는 절대로 안되지만 테스트 환경에서는 전혀 문제없는 방식이 있다.

테스트 당 assert 하나

assert 문이 단 하나인 함수는 결론이 하나라서 코드를 이해하기 쉽고 빠르다. assert 문 개수는 최대한 줄여야 좋다. 이는 테스트 함수마다 한 개념만 테스트하라는 규칙인 것이다. 이것저것 잡다한 개념을 연속으로 테스트하는 긴 함수는 피하는 것이 좋다.

깨끗한 테스트는 다음 다섯 가지 규칙을 따른다.

F.I.R.S.T

빠르게(Fast): 테스트는 빨라야 한다.
독립적으로(Independent): 각 테스트 서로 의존하면 안된다.
반복가능하게(Repeatable): 테스트는 어떤 환경에서도 반복 가능해야 한다.
자가검증하는(Self-Validating): 테스트는 부울(Bool) 값으로 결과를 내야한다. (성공 or 실패)
적시에(Timely): 테스트는 적시에 작성해야 한다. (실제 코드를 구현한 다음에 테스트 코드를 만들면 실제 코드가 테스트하기 어렵다는 사실을 발견할지도 모른다.)

느낀 점

9장을 통해 테스트 코드가 어쩌면 실제코드보다 더 중요하다는 것을 알게 되었다. 그래서 협업 개발자들이 테스트 코드를 작성하는 것이 중요하다고 하는 구나 라고 느꼈다. 또한 테스트 코드를 작성하기 위해서는 가독성이 정말로 중요하며, 우리가 왜 given-when-then 패턴을 사용했는지도 알아보았다.

테스트 코드를 작성하는 것이 일이 더 늘어난다고 생각할 수 있지만 절대로 그렇지 않다. 짧게 보면 그럴 수 있지만 길게 보면 결국엔 변경이 일어날 것이고, 테스트 코드를 통해 우리는 쉽게 안정감 있게 변경할 수 있을 것이다.

References

'공부 기록' 카테고리의 다른 글

[클린코드] 11장. 시스템  (0) 2024.05.01
[클리코드] 10장. 클래스  (0) 2024.04.26
[클린코드] 8장. 경계  (0) 2024.04.22
[클린코드] 7장. 오류 처리  (1) 2024.04.19
[클린코드] 6장. 객체와 자료구조  (0) 2024.04.18

댓글