1 분 소요

테스트 코드를 작성할 때, 테스트 대역을 생성하거나 특수한 설정을 위해 제품 코드를 수정해야 하는 경우가 있습니다. 그러나 이러한 변경이 실제로 필요한지 신중하게 고민해야 합니다. 테스트를 위해 제품 코드를 수정하는 것은 어디까지 허용되어야 할까요?

사이드 이펙트가 없는 코드

테스트에서 SUT(System Under Test)를 검증하기 위해 SUT의 상태를 얻어오는 인터페이스가 필요한 경우가 있습니다. 제품 코드에는 해당 상태를 조회할 필요가 없지만, 테스트에서는 이 정보를 활용하여 검증해야 할 때가 있습니다. 이때 이 인터페이스가 해당 모듈의 명확한 역할에 포함된다면, 연산을 SUT에 넣어도 됩니다.

테스트 대역을 위한 코드가 필요한 경우도 있습니다. 테스트 대역을 생성하기 위해 상속이 불가능한 SUT를 상속이 가능하게 변경하는 경우도 있고, 테스트 대역을 생성하기 위한 코드가 추가가 되어야 할 수도 있습니다. 이러한 상황에서 제품 코드를 수정하는 것은 언어적, 기술적으로 필요할 수 있습니다.

이렇게 기술적인 요구에 부응하기 위해 테스트에 필요한 코드가 제품 코드에 추가되어야 하는 경우가 있습니다. 설계에 영향을 주지 않는다면 크게 문제가 없지만, 이런 방식이 반복될 경우 근본적인 설계에 문제가 있는지 한번쯤 고민을 해야 합니다.

사이드 이펙트가 있는 코드

가끔은 테스트를 위한 복잡한 구현을 감추는 파서드나 테스트 데이터를 생성하는 로직이 필요합니다. 많은 경우에 이런 연산은 SUT의 책임을 넘어 테스트 도메인에 포함이 됩니다.

이러한 경우엔 우선 해당 연산이 SUT의 본질적인 책임인지 아닌지 고민하고, 테스트 도메인에 속한다면 테스트 모듈 안에 위치시켜야 합니다. 나중에 해당 기능이 다른 모듈에서 필요하면 적절히 추상화하여 확장할 수 있습니다.

라이브러리 주도 개발 (Library driven development)은 각 모듈을 라이브러리의 관점에서 구현합니다. 현재 작업중인 모듈, 라이브러리에 코드를 작성하면서 구조적인 통일성이나 모듈의 책임을 명확히 하기 위해 기능을 추가하고 싶을 수도 있습니다. 하지만 실제로 다른 모듈에서 기능이 필요한 시점에 추가하는 것이 좋습니다.

언어와 구현은 통일되어야 합니다. 아직 언어 상으로 명확히 표현되지 않는 기능은 쉽게 변경될 수 있어 기존에 구현된 기능과 큰 차이가 생길 수 있습니다. 이미 구현된 기능은 새로운 기능을 추가하거나 변경하는데 보수적으로 생각하게 만들어서 도메인 모델을 정체되게 합니다.

언어 간의 충돌

테스트에는 테스트를 위한 지식이 포함되어 있어 테스트 코드와 제품 코드는 약간은 서로 다른 언어를 사용합니다. 하지만 언어를 공유하는 부분에서는 동일한 언어를 사용해야 합니다.

테스트 코드가 제품 코드의 설계를 변경한다면, 모듈의 역할과 책임을 훼손하고, 언어와 구현의 충돌이 발생할 수 있습니다. 이런 언어와 구현의 불일치는 언어의 변화에 구현이 대응하는 것을 어렵게 만들고, 사용하는 언어의 차이를 만들어 의사소통의 어려움을 만듭니다. 이러한 충돌을 신중하게 다뤄야 하며, 언어 간의 불일치는 설계의 잘못된 방향으로 나아가고 있다는 신호일 수 있습니다.

테스트 코드가 설계를 바꾸지 않을 경우에만 제품 코드를 수정할 수 있습니다. 테스트는 언어와 설계를 변경해서는 안 되며, 테스트 코드에서만 사용되는 제품 코드는 기존의 모듈의 설계와 책임을 유지해야 합니다. 언어 간의 일관성을 유지하면서 테스트를 작성해야 구현을 이해하고 변경에 대응하기가 쉬워집니다.

댓글남기기