TDD에서 프로덕션 코드를 수정하지 않고 통과 한 테스트 사례를 작성하면 무엇을 의미합니까?


17

다음은 TDD에 대한 Robert C. Martin의 규칙입니다 .

  • 실패한 단위 테스트를 통과하지 않으면 프로덕션 코드를 작성할 수 없습니다.
  • 실패하기에 충분한 단위 테스트를 더 이상 작성할 수 없습니다. 컴파일 실패는 실패입니다.
  • 실패한 단위 테스트를 통과하기에 충분한 양보다 더 많은 생산 코드를 작성할 수 없습니다.

가치가 있지만 테스트 코드를 변경하지 않고 통과하는 테스트를 작성할 때 :

  1. 내가 잘못했음을 의미합니까?
  2. 도움이 될 수 있다면 앞으로 이러한 테스트를 작성하지 않아야합니까?
  3. 그 시험을 그 곳에 두거나 제거해야합니까?

참고 : 나는 이 질문을 여기 에서 시도 하고 있었다 : 통과하는 단위 테스트로 시작할 수 있습니까? 그러나 지금까지 질문을 충분히 설명 할 수 없었습니다.


인용 한 기사에 링크 된 "볼링 게임 카타"는 실제로 최종 단계로 즉시 통과하는 테스트를 거칩니다.
jscs

답변:


21

그것은 실패한 단위 테스트를 통과하지 않으면 프로덕션 코드를 작성할 수 없으며 시작부터 통과하는 테스트를 작성할 수는 없다고 말합니다. 규칙의 의도는 "제작 코드를 편집해야하는 경우 먼저 테스트를 작성하거나 테스트해야합니다."라고 말합니다.

때때로 우리는 이론을 증명하기 위해 테스트를 작성합니다. 시험은 통과하고 우리의 이론을 반증합니다. 그런 다음 테스트를 제거하지 않습니다. 그러나 (소스 제어의 지원을 알고 있음) 프로덕션 코드가 중단되어 예상하지 못한 코드가 통과 한 이유를 이해할 수 있습니다.

테스트가 유효하고 올바른 것으로 판명되고 기존 테스트와 중복되지 않는 경우 그대로 두십시오.


기존 코드의 테스트 범위를 향상시키는 것은 (희망스럽게) 합격 테스트를 작성하는 또 다른 완벽한 이유입니다.
Jack

13

다음 중 하나를 의미합니다.

  1. 먼저 테스트를 작성하지 않고 원하는 기능을 수행하는 프로덕션 코드를 작성했거나 ( "종교적 TDD"위반) 또는
  2. 필요한 기능은 이미 프로덕션 코드로 이행되었으며 해당 기능을 다루기 위해 다른 단위 테스트를 작성하고 있습니다.

후자의 상황은 생각보다 일반적입니다. 완전히 불분명하고 사소한 (아직도 여전히 예시적인) 예로써, 다음과 같은 단위 테스트 (가짜이기 때문에 의사 코드)를 작성했다고 가정 해 봅시다.

public void TestAddMethod()
{
    Assert.IsTrue(Add(2,3) == 5);
}

실제로 필요한 것은 2와 3을 더한 결과입니다.

구현 방법은 다음과 같습니다.

public int add(int x, int y)
{
    return x + y;
}

그러나 이제 4와 6을 더해야한다고 가정 해 봅시다.

public void TestAddMethod2()
{
    Assert.IsTrue(Add(4,6) == 10);
}

나는 이미 두 번째 경우를 다루기 때문에 내 방법을 다시 작성할 필요가 없습니다.

이제 Add 함수가 실제로 한도를 가진 숫자를 반환해야한다는 것을 알았습니다 .100이라고 가정 해 보겠습니다.이를 테스트하는 새로운 메소드를 작성할 수 있습니다.

public void TestAddMethod3()
{
    Assert.IsTrue(Add(100,100) == 100);
}

그리고이 테스트는 이제 실패 할 것입니다. 이제 내 기능을 다시 작성해야합니다

public int add(int x, int y)
{
    var a = x + y;
    return a > 100 ? 100 : a;
}

통과시키기 위해.

상식에 따르면

public void TestAddMethod2()
{
    Assert.IsTrue(Add(4,6) == 10);
}

통과하면 테스트가 실패하도록 새 코드를 작성할 수 있도록 실패한 테스트를 가질 수 있도록 의도적으로 메소드를 실패 하게 하지 않습니다 .


5
Martin의 예제를 완벽하게 따르고 (그가 반드시 그렇게 제안하지는 않습니다) add(2,3)통과하려면 문자 그대로 5를 반환합니다. 그런 다음 테스트 add(4,6)를 작성하면 동시에 중단하지 않고 통과시키는 프로덕션 코드를 작성하게 add(2,3)됩니다. 당신은 것입니다 으로 return x + y,하지만 당신은 그것으로 시작하지 않을 것입니다. 이론에 의하면. 당연히 Martin (또는 다른 사람 일 수도 있습니다. 기억 나지 않습니다)은 교육을위한 그러한 예를 제공하는 것을 좋아하지만 실제로 그러한 사소한 코드를 그런 식으로 작성할 것으로 기대하지는 않습니다.
Anthony Pegram

1
@tieTYT, 일반적으로 Martin의 책에서 올바르게 기억한다면 두 번째 테스트 사례는 일반적으로 간단한 방법에 대한 일반적인 솔루션을 작성하기에 충분합니다 (실제로는 실제로 작동하도록 만들 것입니다) 처음으로). 세 번째 필요가 없습니다.
Anthony Pegram

2
@tieTYT, 그때까지 테스트를 계속 작성합니다. :)
Anthony Pegram

4
세 번째 가능성이 있으며, 귀하의 예와 반대입니다. 중복 테스트를 작성했습니다. TDD를 "상대적으로"따르는 경우, 통과하는 새로운 테스트는 항상 빨간색 플래그입니다. DRY 후에 는 본질적으로 동일한 것을 테스트하는 두 가지 테스트를 작성해서는 안됩니다.
congusbongus

1
"만약 당신이 Martin의 예제를 완벽하게 따르고 (그리고 그가 당신에게 반드시 그렇게하도록 제안하지는 않습니다) add (2,3)를 통과 시키려면 문자 그대로 5를 반환 할 것입니다." -이것은 항상 나와 함께 강판 한 엄격한 TDD 입니다. 향후의 테스트를 기대하고 증명할 때 당신이 알고있는 코드를 작성한다는 생각 은 잘못 입니다. 어떤 이유로 미래의 테스트가 작성되지 않고 동료가 "모든 테스트 녹색"이 "모든 코드 수정"을 의미한다고 가정하면 어떻게됩니까?
Julia Hayward

2

당신의 시험은 통과하지만 당신은 틀리지 않습니다. 프로덕션 코드가 처음부터 TDD가 아니기 때문에 발생했다고 생각합니다.

정식 (?) TDD를 가정 해 봅시다. 프로덕션 코드는 없지만 몇 가지 테스트 사례가 있습니다 (물론 항상 실패합니다). 전달할 프로덕션 코드를 추가합니다. 그런 다음 여기서 더 이상 실패한 테스트 사례를 추가하십시오. 다시 전달할 프로덕션 코드를 추가하십시오.

다시 말해, 테스트는 단순한 TDD 단위 테스트가 아닌 일종의 기능 테스트 일 수 있습니다. 이는 제품 품질에있어 항상 귀중한 자산입니다.

나는 개인적으로 전체주의적이고 비인간적 인 규칙을 좋아하지 않습니다.


2

실제로 지난 밤 도조에서 같은 문제가 발생했습니다.

나는 그것에 대해 빠른 연구를했다. 이것이 내가 생각해 낸 것입니다.

기본적으로 TDD 규칙에 의해 명시 적으로 금지되지 않습니다. 함수가 일반화 된 입력에 대해 올바르게 작동 함을 증명하기 위해 추가 테스트가 필요할 수 있습니다. 이 경우 TDD 실습은 잠시 동안 그대로 유지됩니다. 그 사이에 생산 코드가 추가되지 않는 한 TDD 실습을 짧게 남겨 두는 것이 반드시 TDD 규칙을 어기는 것은 아닙니다.

중복되지 않는 한 추가 테스트를 작성할 수 있습니다. 동등한 클래스 분할 테스트를 수행하는 것이 좋습니다. 즉, 모든 동등성 등급에 대한 엣지 케이스와 하나 이상의 내부 케이스가 테스트됨을 의미합니다.

그러나이 방법으로 발생할 수있는 한 가지 문제는 테스트가 처음부터 통과되면 오 탐지가 없다는 것을 확신 할 수 없다는 것입니다. 테스트가 올바르게 구현되지 않고 프로덕션 코드가 올바르게 작동하지 않아 테스트가 통과 할 수 있음을 의미합니다. 이를 방지하려면 테스트를 중단하기 위해 생산 코드를 약간 변경해야합니다. 이로 인해 테스트가 실패하면 테스트가 올바르게 구현되고 프로덕션 코드를 다시 변경하여 테스트를 다시 통과시킬 수 있습니다.

엄격한 TDD를 연습하고 싶다면 처음부터 통과하는 추가 테스트를 작성하지 않을 수 있습니다. 반면에 엔터프라이즈 개발 환경에서는 추가 테스트가 유용 할 경우 실제로 TDD 실습을 떠나야합니다.


0

프로덕션 코드를 수정하지 않고 통과하는 테스트는 본질적으로 나쁘지 않으며 추가 요구 사항이나 경계 사례를 설명하는 데 종종 필요합니다. 귀하의 시험이 "가치있는 것"으로 판단되는 한, 귀하의 시험은 유지하십시오.

문제가 발생하는 곳은 문제 영역을 실제로 이해하기위한 대안으로 이미 통과 한 테스트를 작성할 때입니다.

우리는 두 가지 극단적 인 상황을 상상할 수있다. 하나의 프로그래머가 "만약을 대비하여"많은 수의 테스트를 작성하면 하나의 버그를 발견 할 수있다. 최소한의 테스트를 작성하기 전에 문제 공간을 신중하게 분석하는 두 번째 프로그래머. 둘 다 절대 값 함수를 구현하려고한다고 가정 해 봅시다.

첫 번째 프로그래머는 다음과 같이 씁니다.

assert abs(-88888) == 88888
assert abs(-12345) == 12345
assert abs(-5000) == 5000
assert abs(-32) == 32
assert abs(46) == 46
assert abs(50) == 50
assert abs(5001) == 5001
assert abs(999999) == 999999
...

두 번째 프로그래머는 다음과 같이 씁니다.

assert abs(-1) == 1
assert abs(0) == 0
assert abs(1) == 1

첫 번째 프로그래머의 구현 결과는 다음과 같습니다.

def abs(n):
    if n < 0:
        return -n
    elif n > 0:
        return n

두 번째 프로그래머의 구현 결과는 다음과 같습니다.

def abs(n):
    if n < 0:
        return -n
    else:
        return n

모든 테스트는 통과했지만 첫 번째 프로그래머는 여러 중복 테스트를 작성했을뿐 아니라 (개발주기를 늦출 필요없이) 경계 사례를 테스트하지 못했습니다 ( abs(0)).

프로덕션 코드를 수정하지 않고 통과하는 테스트를 작성하는 경우 테스트가 실제로 가치를 추가하는지 또는 문제 공간을 이해하는 데 더 많은 시간을 소비해야하는지 스스로에게 문의하십시오.


동료 프로그래머가 재정의 abs(n) = n*n하고 통과 했기 때문에 두 번째 프로그래머도 테스트에 부주의했습니다 .
Eiko

@Eiko 당신은 절대적으로 맞습니다. 너무 적은 테스트를 작성 하면 심하게 물릴 수 있습니다. 두 번째 프로그래머는 최소한 테스트를하지 않아 지나치게 인색했습니다 abs(-2). 모든 것과 마찬가지로 중재가 핵심입니다.
thinkterry
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.