구현을 작성한 후 테스트에서 실수를 수정하는 방법


21

로직을 올바르게 구현 한 후에도 테스트에 여전히 실패한 경우 (테스트에 실수가 있기 때문에) TDD에서 가장 좋은 조치는 무엇입니까?

예를 들어, 다음 기능을 개발하려고한다고 가정하십시오.

int add(int a, int b) {
    return a + b;
}

다음 단계에서 개발한다고 가정하십시오.

  1. 쓰기 테스트 (아직 기능 없음) :

    // test1
    Assert.assertEquals(5, add(2, 3));
    

    컴파일 오류가 발생합니다.

  2. 더미 함수 구현을 작성하십시오.

    int add(int a, int b) {
        return 5;
    }
    

    결과 : test1합격.

  3. 다른 테스트 사례를 추가하십시오.

    // test2 -- notice the wrong expected value (should be 11)!
    Assert.assertEquals(12, add(5, 6));
    

    결과 : test2실패, test1여전히 통과합니다.

  4. 실제 구현 작성 :

    int add(int a, int b) {
        return a + b;
    }
    

    결과 : test1여전히 통과 test2하고 실패합니다 (이후부터 11 != 12).

이 특별한 경우에 :

  1. 수정 test2하고 이제 통과하는지 확인하십시오. 또는
  2. 구현의 새 부분을 삭제하고 (즉, 위의 2 단계로 돌아가) 수정 test2하고 실패한 다음 올바른 구현을 다시 소개하십시오 (위의 4 단계).

아니면 다른 영리한 방법이 있습니까?

예제 문제가 다소 사소한 것으로 이해하지만 일반적인 경우에 수행해야 할 작업에 관심이 있습니다. 두 숫자를 더하는 것보다 더 복잡 할 수 있습니다.

편집 (@Thomas Junk의 답변에 대한 답변) :

이 질문의 초점은 그러한 경우 TDD가 제안하는 것이지, 좋은 코드 나 테스트 (TDD-way와 다를 수 있음)를 달성하기위한 "범용 모범 사례"가 아닙니다.



5
분명히, 당신은 당신의 TDD에서 TDD를하고 있어야합니다.
Blrfl

17
내가 왜 TDD에 대해 회의적인지 물어 보는 사람이 있다면 나는이 질문을 지적 할 것이다. 이것은 Kafkaesque입니다.
Traubenfuchs

Xibit이 우리에게 무엇을의가»내가 TDD에서 TDD를 넣어 @Blrfl 있도록 할 수 TDD«TDDing 동안 : D
토마스 정크

3
@Traubenfuchs 나는 첫눈에 문제가 어리석은 것처럼 보이고 "항상 TDD를하라"고 옹호자가 아니지만 테스트 실패를 보았을 때 강한 이점이 있다고 생각하고 테스트를 통과시키는 코드를 작성합니다. (이것은 결국이 질문에 관한 것입니다).
Vincent Savard

답변:


31

절대적으로 중요한 것은 테스트가 통과하고 실패하는 것입니다.

테스트를 실패로 만들기 위해 코드를 삭제했는지 여부에 관계없이 코드를 다시 작성하거나 클립 보드로 가져와 나중에 다시 붙여 넣는 것은 중요하지 않습니다. TDD는 다시 입력해야한다고 말한 적이 없습니다. 테스트가 통과해야 할 때만 통과하고 실패해야 할 때만 실패한다는 것을 알고 싶습니다.

테스트를 통과 한 것과 실패한 것을 보는 것이 테스트를 테스트하는 방법입니다. 두 번 다 본 적이없는 테스트를 절대 신뢰하지 마십시오.


빨간색 막대에 대한 리팩토링은 작동 테스트를 리팩토링하기위한 공식적인 단계를 제공합니다.

  • 테스트를 실행
    • 녹색 막대 참고
    • 테스트중인 코드 중단
  • 테스트를 실행
    • 빨간 막대에 주목
    • 테스트 리팩터링
  • 테스트를 실행
    • 빨간 막대에 주목
    • 테스트중인 코드 해제
  • 테스트를 실행
    • 녹색 막대 참고

그러나 우리는 작동 테스트를 리팩토링하지 않습니다. 버기 테스트를 바꿔야합니다. 한 가지 우려는이 테스트 만 다루면서 소개 된 코드입니다. 테스트가 수정되면 이러한 코드를 롤백하고 다시 소개해야합니다.

그렇지 않은 경우 코드를 다루는 다른 테스트로 인해 코드 적용 범위가 문제가되지 않으면 테스트를 변환하여 녹색 테스트로 도입 할 수 있습니다.

여기서 코드도 롤백되지만 테스트가 실패 할 정도로 충분합니다. 버그 테스트로만 커버되는 동안 소개 된 모든 코드를 다루기에 충분하지 않으면 더 큰 코드 롤백과 더 많은 테스트가 필요합니다.

친환경 테스트 소개

  • 테스트를 실행
    • 녹색 막대 참고
    • 테스트중인 코드 중단
  • 테스트를 실행
    • 빨간 막대에 주목
    • 테스트중인 코드 해제
  • 테스트를 실행
    • 녹색 막대 참고

코드를 해제하면 코드를 주석 처리하거나 나중에 다시 붙여 넣기 위해 다른 곳으로 이동시킬 수 있습니다. 이것은 테스트가 다루는 코드의 범위를 보여줍니다.

이 마지막 두 번의 달리기를 위해 당신은 정상적인 빨강 녹색 주기로 돌아갑니다. 코드를 해제하고 테스트를 통과하기 위해 입력하는 대신 붙여 넣기 만하면됩니다. 따라서 시험에 합격하기에 충분한 양만 붙여 넣어야합니다.

여기서 전반적인 패턴은 테스트 색상이 예상 한대로 변경되는 것을 보는 것입니다. 이렇게하면 신뢰할 수없는 녹색 테스트가 잠깐 발생하는 상황이 발생합니다. 이 단계에서 중단되고 잊어 버리지 않도록주의하십시오.

Red Bar 수용 링크에 대한 RubberDuck 에게 감사드립니다 .


2
나는이 답변을 가장 좋아합니다. 잘못된 코드로 테스트가 실패하는 것을 보는 것이 중요하므로 코드를 삭제 / 주석 처리하고 테스트를 수정하고 실패한 것을 확인하고 코드를 다시 넣습니다 (테스트를하는 데 의도적 인 오류가 발생할 수 있음) 테스트)하고 올바르게 작동하도록 코드를 수정하십시오. 완전히 삭제하고 다시 작성하는 것은 매우 XP이지만 때로는 실용적이어야합니다. ;)
GolezTrol 2016 년

@ GolesTrol 내 대답은 똑같다고 생각하므로 명확하지 않은지 여부에 대한 의견을 보내 주셔서 감사합니다.
jonrsharpe

@ jonrsharpe 귀하의 답변도 훌륭하며,이 답변을 읽기 전에 찬성했습니다. 그러나 코드를 되 돌리는 것이 매우 엄격한 경우 CandiedOrange는 더 실용적 인 접근 방식을 제안합니다.
GolezTrol

@GolezTrol 나는 코드를 되 돌리는 방법 을 말하지 않았다 . 주석 처리, 잘라 내기 및 붙여 넣기, 숨김, IDE 기록 사용; 정말 중요하지 않습니다. 결정적인 이유는 그렇게 하는 것입니다. 따라서 올바른 실패를 겪고 있는지 확인할 수 있습니다 . 분명히하기 위해 편집했습니다.
jonrsharpe

10

달성하고자 하는 전반적인 목표 는 무엇입니까 ?

  • 좋은 테스트를하고 있습니까?

  • 만들기 올바른 구현을?

  • TTD가 종교적으로 옳 습니까?

  • 위의 어느 것도?

아마도 당신은 테스트 및 테스트와의 관계를 지나치게 생각할 것입니다.

테스트 는 구현 의 정확성 에 대해 보증하지 않습니다 . 모든 테스트에 합격하면 소프트웨어가 원하는대로 작동하는지 여부에 대해서는 아무 것도 말하지 않습니다. 소프트웨어에 대해 본질적인 언급 을하지 않습니다 .

예를 들면 :

추가의 "올바른"구현은 코드와 동일합니다 a+b. 그리고 코드 그렇게하는 한 알고리즘이 수행하는 작업이 정확 하고 올바르게 구현 되었다고 말할 수 있습니다 .

int add(int a, int b) {
    return a + b;
}

첫눈 , 우리 모두는이 것을, 동의 할 것 입니다 부가의 구현입니다.

그러나 우리가 정말하고있는 것은이 코드가 있다는 말을하지 않는 것입니다 의 구현 addition어느 정도에만 동작합니다 같은이 : 생각 정수 오버 플로우 .

정수 오버플로 코드 에서는 발생 하지만의 개념 에서는 발생하지 않습니다 addition. 따라서 : 코드는의 개념과 같은 정도로 작동 addition하지만 그렇지 않습니다 addition.

이 다소 철학적 인 관점에는 몇 가지 결과가 있습니다.

그리고 테스트는 코드의 예상되는 동작에 대한 가정 에 지나지 않습니다 . 당신의 코드를 테스트에서, 당신은 (아마도) 확인, 당신 할 수 없었 구현 입니다 바로 , 당신이 말할 수있는 최선이다, 당신의 코드가 있었다 전달한다 또는 충족되지 않은 어떤 결과에 기대; 코드가 잘못되었거나 테스트가 잘못되었거나 코드가 잘못되었다는 것입니다.

유용한 테스트를 통해 코드의 기능에 대한 기대치 를 수정하는 데 도움이됩니다 . 기대치를 변경하지 않고 수정 된 코드가 예상 한 결과를 제공하는 한, 내가 가정 한 내용이 확실하다고 확신 할 수 있습니다 결과는 잘 작동하는 것 같습니다.

잘못된 가정을했을 때 도움이되지 않습니다. 하지만 이봐! 적어도 정신 분열증을 예방합니다 : 아무것도 없어야 할 때 다른 결과를 기대합니다.


tl; dr

로직을 올바르게 구현 한 후에도 테스트에 여전히 실패한 경우 (테스트에 실수가 있기 때문에) TDD에서 가장 좋은 조치는 무엇입니까?

테스트는 코드의 동작에 대한 가정입니다. 구현이 옳다고 생각할만한 충분한 이유가 있다면 테스트를 수정하고 해당 가정이 적용되는지 확인하십시오.


1
전반적인 목표에 대한 질문은이를 제기 해 주셔서 감사합니다. 나를 위해, 가장 높은 prio는 다음과 같습니다. 1. 올바른 구현 2. "좋은"테스트 (또는 "유용한"/ "좋은 디자인 된"테스트). TDD를 이러한 두 가지 목표를 달성하기위한 가능한 도구로보고 있습니다. 따라서 반드시 종교적으로 TDD를 따르고 싶지는 않지만이 질문의 맥락에서 TDD 관점에 주로 관심이 있습니다. 이를 명확하게하기 위해 질문을 편집하겠습니다.
Attilio

따라서 오버플로를 테스트하고 발생할 때 통과하는 테스트를 작성합니까, 아니면 알고리즘이 추가되고 오버플로가 잘못된 응답을 생성하여 오버플로가 발생하면 실패하게됩니까?
Jerry Jeremiah

1
@JerryJeremiah 내 요점은 다음과 같습니다. 테스트에서 다루어야 할 것은 사용 사례에 따라 다릅니다. 한 자리 숫자를 더하는 유스 케이스의 경우 알고리즘이 충분 합니다. 알고 있다면 "큰 숫자"를 더할 가능성이 높다는 datatype것은 분명 잘못된 선택입니다. 테스트 결과는 다음과 같습니다. 귀하의 기대는»많은 숫자에 적합 함«이며 몇몇 경우에는 충족되지 않습니다. 그렇다면 문제는 그러한 경우를 처리하는 방법입니다. 코너 케이스입니까? 그렇다면 어떻게 처리합니까? 아마도 일부 quard 절은 더 큰 혼란을 방지하는 데 도움이됩니다. 답은 맥락에 달려있다.
토마스 정크

7

구현이 잘못되면 테스트가 실패한다는 것을 알아야합니다. 구현이 옳은 경우 통과와 동일하지 않습니다. 따라서 테스트 수정 하기 전에 코드가 실패 할 것으로 예상되는 상태로 코드를 다시 배치하고 예상 5 != 12하지 못한 다른 이유가 아니라 예상 한 이유로 (예 :) 코드가 실패하는지 확인해야합니다 .


예상 한 이유로 테스트가 실패한 것을 어떻게 확인할 수 있습니까?
Basilevs 2012 년

2
@Basilevs you : 1. 실패 이유가 무엇인지에 대한 가설을 세우십시오. 2. 테스트를 실행하십시오. 3. 결과적인 실패 메시지를 읽고 비교하십시오. 때로는 더 의미있는 오류를 제공하기 위해 테스트를 다시 작성할 수있는 방법을 제안하기도합니다 (예 : 둘 다 동일한 것을 테스트하는 assertTrue(5 == add(2, 3))경우보다 덜 유용한 출력을 제공함 assertEqual(5, add(2, 3))).
jonrsharpe

여기에서이 원칙을 어떻게 적용할지는 아직 불분명합니다. 가설이 있습니다-테스트가 일정한 값을 반환합니다. 동일한 테스트를 다시 실행하면 내가 옳다는 것을 어떻게 보장합니까? 분명히 테스트하려면 다른 테스트가 필요합니다. 대답하기 위해 명확한 예를 추가하는 것이 좋습니다.
Basilevs 2012 년

1
@Basilevs 무엇? 여기서 3 단계의 가설은 "5는 12와 같지 않기 때문에 테스트가 실패합니다" 입니다. 테스트를 실행하면 테스트가 실패한 이유, 진행중인 경우 또는 다른 이유로 인해 실패한 이유가 표시됩니다. 어쩌면 이것은 언어 문제 일지 모르지만 당신이 제안하는 것이 확실하지 않습니다.
jonrsharpe

5

이 특별한 경우, 12를 11로 변경하고 테스트가 통과하면 테스트뿐만 아니라 구현 테스트를 잘 수행 했으므로 추가 후프를 수행 할 필요가 없습니다.

그러나 설정 코드에 실수가있는 경우와 같이보다 복잡한 상황에서도 동일한 문제가 발생할 수 있습니다. 이 경우 테스트를 수정 한 후 특정 테스트가 실패하도록 돌연변이를 구현 한 다음 구현을 변경해야합니다. 구현을 되 돌리는 것이 가장 쉬운 방법이라면 괜찮습니다. 귀하의 예제에서는 변이 수 a + ba + aa * b.

또는 어설 션을 약간 변경하고 테스트 실패를 확인할 수 있으면 테스트 테스트에 매우 효과적 일 수 있습니다.


0

나는 이것이 당신이 선호하는 버전 관리 시스템의 경우라고 말합니다.

  1. 작업 디렉토리에서 코드 변경 사항을 유지하면서 테스트 수정을 준비하십시오.
    해당 메시지로 커밋합니다 Fixed test ... to expect correct output.

    를 사용 하면 테스트 및 구현이 동일한 파일 gitgit add -p있는지 여부를 사용해야 할 수도 있습니다 . 그렇지 않으면 두 파일을 별도로 준비 할 수 있습니다.

  2. 구현 코드를 커밋하십시오.

  3. 시간이 지남에 따라 1 단계에서 수행 한 커밋을 테스트하여 테스트가 실제로 실패하는지 확인하십시오 .

당신은 당신이 실패한 테스트를 테스트하는 동안 구현 코드를 방해하지 않기 위해 편집 능력에 의존하지 않는 방식을 알 수 있습니다. VCS를 사용하여 작업을 저장하고 VCS 기록 히스토리에 실패 및 통과 테스트가 모두 올바르게 포함되도록합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.