[unit-testing] 중복 된 코드는 단위 테스트에서 더 견딜 수 있습니까?

나는 얼마 전 여러 단위 테스트를 망 쳤고 그것들을 더 DRY 로 만들기 위해 리팩토링했습니다. 각 테스트의 의도는 더 이상 명확하지 않았습니다. 테스트의 가독성과 유지 보수성간에 절충안이있는 것 같습니다. 단위 테스트에 중복 된 코드를 남겨두면 더 읽기 쉬워 지지만 SUT를 변경하면 중복 된 코드의 각 사본을 추적하고 변경해야합니다.

이 절충안이 존재한다는 데 동의하십니까? 그렇다면 테스트를 읽을 수 있거나 유지 관리 할 수있는 것을 선호합니까?



답변

중복 된 코드는 다른 코드와 마찬가지로 단위 테스트 코드에서도 냄새가납니다. 테스트에서 중복 된 코드가있는 경우 업데이트 할 테스트 수가 불균형하기 때문에 구현 코드를 리팩토링하기가 더 어려워집니다. 테스트는 테스트중인 코드에 대한 작업을 방해하는 큰 부담이 아니라 자신있게 리팩터링하는 데 도움이됩니다.

복제가 조명기 설정에있는 경우 setUp방법 을 더 많이 사용 하거나 더 많은 (또는 더 유연한) 생성 방법을 제공하는 것을 고려하십시오 .

중복이 SUT를 조작하는 코드에있는 경우 소위 여러 “단위”테스트가 정확히 동일한 기능을 실행하는 이유를 스스로에게 물어보십시오.

중복이 어설 션에있는 경우 사용자 지정 어설 ​​션 이 필요할 수 있습니다 . 예를 들어 여러 테스트에 다음과 같은 어설 션 문자열이있는 경우

assertEqual('Joe', person.getFirstName())
assertEqual('Bloggs', person.getLastName())
assertEqual(23, person.getAge())

그렇다면 아마도 단일 assertPersonEqual메소드 가 필요할 것 assertPersonEqual(Person('Joe', 'Bloggs', 23), person)입니다. (또는 단순히에서 같음 연산자를 오버로드해야 할 수도 있습니다 Person.)

언급했듯이 테스트 코드를 읽을 수 있어야합니다. 특히 테스트 의 의도 가 분명한 것이 중요합니다 . 많은 테스트가 거의 동일하게 보이는 경우 (예 : 줄의 3/4이 동일하거나 거의 동일)주의 깊게 읽고 비교하지 않고 중요한 차이점을 찾아 내고 인식하기가 어렵습니다. 따라서 모든 테스트 방법의 모든 행이 테스트 목적과 직접 관련이 있기 때문에 중복을 제거하기위한 리팩토링 가독성에 도움이 된다는 것을 알게되었습니다 . 이것은 독자들에게 직접적으로 관련된 줄과 단지 상용구 인 줄의 무작위 조합보다 훨씬 더 유용합니다.

즉, 때때로 테스트는 유사하지만 여전히 상당히 다른 복잡한 상황을 실행하며 중복을 줄이는 좋은 방법을 찾기가 어렵습니다. 상식 사용 : 테스트를 읽을 수 있고 의도를 명확하게하고 테스트에서 호출 한 코드를 리팩토링 할 때 이론적으로 최소한의 테스트 이상을 업데이트해야하는 것에 익숙하다면 불완전 성을 받아들이고 이동합니다. 좀 더 생산적인 것입니다. 나중에 영감이 떠오르면 언제든지 돌아와서 테스트를 리팩토링 할 수 있습니다!


답변

가독성은 테스트에서 더 중요합니다. 테스트가 실패하면 문제가 분명해지기를 원합니다. 개발자는 정확히 무엇이 실패했는지 확인하기 위해 많은 요소가 포함 된 테스트 코드를 검토 할 필요가 없습니다. 단위 테스트 테스트를 작성해야 할 정도로 테스트 코드가 너무 복잡 해지는 것을 원하지 않습니다.

그러나 중복을 제거하는 것은 아무것도 모호하게하지 않는 한 일반적으로 좋은 것이며 테스트에서 중복을 제거하면 더 나은 API로 이어질 수 있습니다. 수익이 감소하는 지점을 넘어 가지 않도록하십시오.


답변

구현 코드와 테스트는 동물이 다르며 인수 분해 규칙이 다르게 적용됩니다.

중복 된 코드 또는 구조는 구현 코드에서 항상 냄새입니다. 구현에 상용구를 사용하기 시작하면 추상화를 수정해야합니다.

반면에 테스트 코드는 중복 수준을 유지해야합니다. 테스트 코드의 복제는 두 가지 목표를 달성합니다.

  • 테스트를 분리 된 상태로 유지합니다. 과도한 테스트 결합은 계약이 변경 되었기 때문에 업데이트가 필요한 단일 실패 테스트를 변경하기 어렵게 만들 수 있습니다.
  • 테스트를 독립적으로 의미있게 유지합니다. 단일 테스트가 실패 할 경우 테스트 대상을 정확히 파악하는 것이 합리적으로 간단해야합니다.

각 테스트 방법이 약 20 줄 미만으로 유지되는 한 테스트 코드에서 사소한 중복을 무시하는 경향이 있습니다. 나는 설정-실행-검증 리듬이 테스트 방법에서 명백 할 때를 좋아한다.

테스트의 “확인”부분에서 중복이 발생하면 사용자 지정 어설 ​​션 메서드를 정의하는 것이 도움이되는 경우가 많습니다. 물론 이러한 메서드는 메서드 이름에서 명확하게 확인할 수있는 명확하게 식별 된 관계를 테스트해야합니다 assertPegFitsInHole.-> good, assertPegIsGood-> bad.

테스트 방법이 길고 반복적으로 늘어날 때 가끔 몇 가지 매개 변수를 사용하는 빈칸 채우기 테스트 템플릿을 정의하는 것이 유용하다는 것을 알게됩니다. 그런 다음 실제 테스트 메서드는 적절한 매개 변수가있는 템플릿 메서드에 대한 호출로 축소됩니다.

프로그래밍과 테스트의 많은 부분에 대해서는 명확한 답이 없습니다. 당신은 취향을 개발할 필요가 있으며 그렇게하는 가장 좋은 방법은 실수를하는 것입니다.


답변

나는 동의한다. 트레이드 오프는 존재하지만 장소마다 다릅니다.

상태 설정을 위해 중복 된 코드를 리팩터링 할 가능성이 더 큽니다. 그러나 실제로 코드를 실행하는 테스트 부분을 리팩토링 할 가능성은 적습니다. 즉, 코드를 연습하는 데 항상 여러 줄의 코드가 필요하면 냄새라고 생각하고 테스트중인 실제 코드를 리팩터링 할 수 있습니다. 그리고 그것은 코드와 테스트 모두의 가독성과 유지 보수성을 향상시킬 것입니다.


답변

여러 가지 테스트 유틸리티 방법을 사용하여 반복을 줄일 수 있습니다 .

나는 프로덕션 코드보다 테스트 코드에서 반복에 더 관대하지만 때때로 그것에 대해 좌절감을 느낍니다. 클래스의 디자인을 변경하고 돌아가서 모두 동일한 설정 단계를 수행하는 10 가지 테스트 방법을 조정해야하는 경우 실망 스럽습니다.


답변

Jay Fields는 “DSL은 DRY가 아니라 DAMP 여야합니다”라는 문구를 만들었습니다. 여기서 DAMP설명적이고 의미있는 문구를 의미합니다 . 테스트에도 동일하게 적용된다고 생각합니다. 분명히 너무 많은 중복은 나쁘다. 그러나 어떤 대가를 치르더라도 중복을 제거하는 것은 더 나쁩니다. 테스트는 의도를 드러내는 사양으로 작동해야합니다. 예를 들어, 여러 다른 각도에서 동일한 기능을 지정하면 일정량의 중복이 예상됩니다.


답변

나는 이것 때문에 rspec을 좋아합니다.

두 가지 도움이 필요합니다.

  • 일반적인 행동을 테스트하기위한 공유 예제 그룹.
    테스트 세트를 정의한 다음 실제 테스트에 설정된 ‘포함’할 수 있습니다.

  • 중첩 된 컨텍스트.
    클래스의 모든 테스트가 아니라 테스트의 특정 하위 집합에 대해 기본적으로 ‘설정’및 ‘해체’방법을 가질 수 있습니다.

.NET / Java / 다른 테스트 프레임 워크가 이러한 방법을 빨리 채택할수록 더 좋습니다 (또는 IronRuby 또는 JRuby를 사용하여 테스트를 작성할 수 있습니다. 개인적으로 더 나은 옵션이라고 생각합니다)