본문 바로가기

책읽기/클린코드

단위 테스트

TDD 의 세가지 법칙

 

1. 실패하는 단위 테스트를 작성한다.

2. 실패하는 테스트를 통과할 정도로만 코드를 작성한다.

3. 리펙토링.

 

 

 

 

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

 

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

 

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

테스트 케이스가 있다면 코드가 변경되었을때, 재대로 변경되었는지의 여부를 알기 쉽다.

따라서 잘 짜여진 자동화된 테스트 케이스는 변경을 쉽게 만든다.

(테스트 케이스가 지저분하다면 코드를 변경하기 힘들 것이고, 결국 지저분한 코드가 될 것이다.)

 

 

깨끗한 테스트 코드

 

깨끗한 테스트 코드는 역시 가독성 이 제일 중요하다.

 

BUILD-OPERATE-CHECK 패턴은 가독성을 늘리기에 좋다.

BUILD : 테스트 자료를 만든다.

OPERATE : 테스트 자료를 조작한다.

CHECK : 조작한 결과를 확인한다.

given-when-then 을 관례적으로 주석으로 사용한다.

 

또한 테스트에 진짜 필요한 자료 유형과 함수만 사용하고 나머지는 모두 뺀다.

 

 

도메인에 특화된 테스트 언어 DSL

 

흔히 쓰는 시스템 조작 API 를 사용하는 대신, API 위에다 함수와 유틸리티를 구현한 후 그 함수와 유틸리티를 사용한다.

그렇게 되면 이 API 는 테스트 코드에서 사용하는 특수 API 가 되고,

개발자와 코드를 읽을 독자를 도와주는 도메인 특화 언어가 된다.

(어찌어찌해서 읽기 좋은 코드가 된다는 것 같은데 잘 이해가 되지 않는 부분이다.)

 

 

 

이중 표준

책의 예에서는, 환경 제어 시스템이 반환하는 상태를 읽기 좋은 테스트 코드로 만든다.

 

결과를 "hBCHl" 과 같은 형태로 받아서 쓰는 것은 원래 코드에서는 있어서는 안되는 일이다.

 

하지만 테스트 코드에서 이렇게 작성하게 된다면, 가독성이 크게 높아진다.

 

따라서 Mock 객체를 만들어 getState() 를 정의하고 위와 같은 형태로 비교하는 테스트로 리펙토링 할 수 있다.

 

이중 표준이라는 말은 원래 코드와 테스트 코드의 표준은 즉 구현 방식은 다르다는 것이다.

실제 환경에서는 절대 안되지만 테스트 환경에서는 문제없는 방식이 있다.

 

 

테스트당 assert 하나

힘든 규칙이긴 하다.

하나의 테스트에서 하나의 assert 문을 사용하려면, 테스트를 쪼개야할 것이다.

테스트를 쪼개게는 과정에서, given-when-then 관례를 사용하고, given, when 절에서 중복되는 코드가 많이 발생할 것이다.

 

이럴 때, Template Method 패턴을 사용하여 

given, when 의 공통부분은 부모 클래스에서 정의하고, 서로 다른 when 부분은 각자 자식 클래스를 만들어 구현하면 중복을 잡을 수 있다.

 

또는 @Before 어노테이션을 사용하면, 각 테스트를 실행하기 전에 공통적으로 실행할 구문을 지정할 수 있다.

 

힘든 규칙인 만큼, 이렇게 분리하는 것이 더 난해하고 커질 수도 있다.

 

책에서는, 테스트당 하나의 assert 문을 넣도록 '노력' 하라고 한다.

 

 

테스트 당 개념 하나

하나의 함수에서 여러 개념을 테스트하면 코드를 이해하기 힘들다. (당연 .. )

 

개념 당 assert 문을 최소한으로 줄여라 !
테스트 함수 하나는 하나의 개념만 테스트하라 !

 

 

 

F.I.R.S.T

Fast : 빠르게 실행되는 테스트를 작성해야 한다.

테스트를 빨리빨리 돌려야 초반에 문제를 찾아내고 고치기 편하다.
느린 테스트를 짜면 돌릴 엄두도 안난다.
Independent : 각 테스트는 서로 의존하면 안된다.

한 테스트가 다른 테스트를 실행할 환경을 만들면 안된다.
서로 의존하는 테스트는 어느 부분에서 문제가 있는지 찾기 힘들게 만든다.
Repeatable : 어떤 환경에서도 테스트는 반복 가능해야 한다.

실제 환경, QA 환경, 네트워크가 연결되지 않은 환경, 지하철 환경이든 뭐든 일관성있는 테스트롤 돌릴 수 있어야 한다.
테스트가 실패했을 때, 코드의 결함을 찾아내는 것 외의 다른 핑계거리가 있으면 안된다.
Self-Validating : 테스트는 boolean 값을 결과로 내야 한다.

테스트가 성공 혹은 실패 둘중 하나의 결과를 내야 한다. 결과로 인해 실패했는지 성공했는지 또 비교하는 코드는 좋지 않다.
Timely : 테스트는 적시에 작성해야 한다.

단위 테스트는 실제 코드를 구현하기 바로 전에 작성해야 한다.

나중에 작성해야지 이렇게 미루게 되면 결국 작성한한다.

테스트가 불가능하도록 코드를 짤 수 도 있다.

 

 

 

결론

테스트용 API 즉, 테스트용 함수, 메소드들을 따로 만들자. 

테스트를 위한 API 를 작성하면, 이중 표준을 이용하여 테스트를 보는 사람이 더 쉽게 이해할 수 있다.

이를 DSL 이라고 할 수 있으며, 깨끗한 테스트 코드를 작성하는데에 도움을 준다.

'책읽기 > 클린코드' 카테고리의 다른 글

시스템  (0) 2021.01.18
클래스  (0) 2021.01.15
경계  (0) 2021.01.14
오류 처리  (0) 2021.01.04
객체와 자료 구조  (0) 2021.01.04