tdd

테스트란?

테스트

테스트란?

애플리케이션의 품질과 안정성을 높이기 위해 사전에 결함을 찾아내고 수정하기 위한 행위

주로 특정 모듈(특히 컴포넌트)이 사양에 잘 동작하는지 자동화된 테스트로 검증

테스트 코드의 효과

1. 좋은 설계에 대한 사고를 도와준다

테스트에 대해 고민하다 보면 어떤 컴포넌트를 어떻게 나눠서 좀 더 의존성을 줄여 코드를 작성해 테스트를 용이하게 할지에 대해 고민하게 된다.

2. 테스트 코드를 기반으로 빠르고 안정적이게 리팩토링 할 수 있다

테스트 코드의 피드백을 기반으로 좁은 범위로 리팩토링의 범위를 나누어 조금씩 수정하면서 안정적으로 사이드 이팩트를 방지하면서 개선을 할 수 있다.

한 가지를 수정할 때마다 테스트를 하면, 오류가 생기더라도 변경 폭이 작기 때문에 살펴볼 범위도 좁아서 문제를 찾고 해결하기가 훨씬 쉽다. 이처럼 조금씩 변경하고 매번 테스트 하는 것은 리팩토링 절차의 핵심이다. - 마틴 파울러

3. 좋은 테스트 코드는 애플리케이션의 이해를 돕는 문서가 된다

어떤 의도로 코드가 작성되었는지 이해하도록 도움을 준다.

올바른 테스트 작성을 위한 규칙

1. 인터페이스를 기준으로 테스트를 작성하자

여기서 인터페이스란 서로 다른 클래스 또는 모듈이 상호작용하는 시스템을 말한다 ⇒ 모든 테스트는 세부 구현을 테스트 하는 것 대신 인터페이스(=Public Method)를 기준으로 작성해야 한다.

캡슐화된 내부 구현에 대한 테스트 코드는 수많은 테스트 코드를 양산할 뿐만 아니라 내부 구현과의 강한 의존성 때문에 구현이 조금만 변경되어도 깨지기 쉽고 유지 보수하기도 굉장히 어렵다. ⇒ 예를 들어 리엑트 컴포넌트에 대해서 테스트를 작성할 때 특정 상태를 버튼 같은 UI를 통해 변경하지 않고 직접 일일이 변수를 대입해가며 변경하면 해당 상태의 변수명이 바뀌거나 비슷한 여러 테스트를 진행하려고 하는 경우에 엄청나게 많은 코드를 반복해서 작성해야 한다.

따라서 DOM 이벤트 핸들러를 사용해 인터페이스를 기준으로 검증하는 것이 좋은 테스트 방법이다.

이렇게 되면 ⓐ내부 구현과 종속성이 없으며 캡슐화에 위반되지 않고 ⓑ어떤 행위를 하는지 명확해지고 ⓒ테스트를 설명하기 위한 불필요한 주석이나 설명이 없어진다.

2. 커버리지 보다는 의미있는 테스트인지 고민하자

여기서 커버리지란 테스트 코드가 프로덕션 코드의 몇 %를 검증하고 있는지 나타낸 지표이다. 구문, 분기, 함수, 줄 등을 기준으로 한다.

테스트를 작성하면서 가장 많이 하는 실수 중 하나가 100% 커버리지를 위해 모든 것을 테스트코드로 검증하려고 하는 것이다.

100% 테스트 커버리지를 작성하는 것은 불가능하고, 테스트 작성 실행 유지 보수 측면에서 너무 많은 비용이 발생한다. 또한, 간단한 유틸 함수들은 변경 가능성도 거의 없으며 내부 로직도 너무 간단하기 때문에 억지로 버그 상황을 만들지 않으면 문제가 될 가능성이 없는 경우도 있다.

따라서 100% 커버리지를 위한 테스트보다는 과연 이 코드에 대한 테스트가 의미있는 테스트인지 어떤 범위로 검증해야 효율적으로 검증할 수 있을지 고민하는 것이 훨씬 중요하다.

3. 테스트 코드도 유지 보수의 대상이므로 가독성을 높이자

테스트 코드도 애플리케이션 변화에 따라 끊임없이 변경되고 유지보수되어야 하는 코드이다.

  • 테스트 하고자 하는 내용을 명확하게 적자
  • 하나의 테스트에서는 가급적 하나의 동작만 검증하자 단일 책임 원칙(SRP, Single Responsibility Principle) : 모든 클래스는 하나의 책임을 갖고 그와 관련된 책임을 캡슐화하여 변경에 견고한 코드를 만들어야 한다 너무 많은 동작을 하나의 테스트에서 검증하게 된다면 검증에 필요한 코드들이 많아지고 테스트에 대한 디스크립션도 장황해져서 가독성이 떨어지게 된다. 또한 여러 개를 검증하다 보니 특정 로직만 수정되어도 테스트 자체가 깨져버린다. 심지어 어떤 동작에서 테스트가 실패했는지 파악하기 어려워진다.