복잡한 방법으로 상당히 복잡한 객체를 가지고 있고 테스트를 통과하고 최소한으로 실패하면 최소한으로 작성합니다. 언제 돌아가서 실제 코드를 작성해야합니까? 그리고 다시 테스트하기 전에 얼마나 많은 실제 코드를 작성합니까? 나는 마지막 것이 더 직감이라고 추측합니다.
"돌아가서" "실제 코드"를 쓰지 마십시오. 모두 실제 코드입니다. 당신이 할 것은 돌아가서 다른 테스트 추가입니다 강제로 당신이 변경 새로운 테스트 통과를 만들기 위해 코드를.
다시 테스트하기 전에 얼마나 많은 코드를 작성합니까? 없음 당신은 쓰기 제로 실패한 테스트없이 코드를 강제로 더 많은 코드를 작성 할 수 있습니다.
패턴을 확인 하시겠습니까?
도움이 되길 바라며 간단한 또 다른 예를 살펴 보겠습니다.
Assert.Equal("1", FizzBuzz(1));
쉬운 피지.
public String FizzBuzz(int n) {
return 1.ToString();
}
실제 코드라고 부르는 것이 아닙니다. 변경을 강제하는 테스트를 추가하겠습니다.
Assert.Equal("2", FizzBuzz(2));
우리는 바보 같은 것을 할 수는 if n == 1
있지만 제정신 해결책으로 건너 뛸 것입니다.
public String FizzBuzz(int n) {
return n.ToString();
}
시원한. 이것은 FizzBuzz 이외의 모든 숫자에 적용됩니다. 프로덕션 코드를 강제로 변경하는 다음 입력은 무엇입니까?
Assert.Equal("Fizz", FizzBuzz(3));
public String FizzBuzz(int n) {
if (n == 3)
return "Fizz";
return n.ToString();
}
다시 한번. 아직 통과하지 못한 테스트를 작성하십시오.
Assert.Equal("Fizz", FizzBuzz(6));
public String FizzBuzz(int n) {
if (n % 3 == 0)
return "Fizz";
return n.ToString();
}
그리고 우리는 이제 3의 배수를 모두 다뤘습니다 (5의 배수도 아닙니다).
아직 "버즈"에 대한 테스트를 작성하지 않았으므로 작성해 보겠습니다.
Assert.Equal("Buzz", FizzBuzz(5));
public String FizzBuzz(int n) {
if (n % 3 == 0)
return "Fizz";
if (n == 5)
return "Buzz"
return n.ToString();
}
그리고 다시, 우리는 처리해야 할 다른 사례가 있다는 것을 알고 있습니다.
Assert.Equal("Buzz", FizzBuzz(10));
public String FizzBuzz(int n) {
if (n % 3 == 0)
return "Fizz";
if (n % 5 == 0)
return "Buzz"
return n.ToString();
}
이제 3의 배수도 아닌 5의 배수를 모두 처리 할 수 있습니다.
지금까지는 리팩토링 단계를 무시하고 있었지만 중복되는 부분이 있습니다. 이제 정리해 봅시다.
private bool isDivisibleBy(int divisor, int input) {
return (input % divisor == 0);
}
public String FizzBuzz(int n) {
if (isDivisibleBy(3, n))
return "Fizz";
if (isDivisibleBy(5, n))
return "Buzz"
return n.ToString();
}
시원한. 이제 중복을 제거하고 잘 명명 된 함수를 만들었습니다. 코드를 변경하도록 강요 할 다음 테스트는 무엇입니까? 글쎄, 우리는 숫자를 3과 5로 나눌 수있는 경우를 피했습니다. 지금 적어 봅시다.
Assert.Equal("FizzBuzz", FizzBuzz(15));
public String FizzBuzz(int n) {
if (isDivisibleBy(3, n) && isDivisibleBy(5, n))
return "FizzBuzz";
if (isDivisibleBy(3, n))
return "Fizz";
if (isDivisibleBy(5, n))
return "Buzz"
return n.ToString();
}
테스트는 통과했지만 더 많은 중복이 있습니다. 옵션이 있지만 다시 작성하는 대신 리팩토링 할 수 있도록 "추출 로컬 변수"를 몇 번 적용하겠습니다.
public String FizzBuzz(int n) {
var isDivisibleBy3 = isDivisibleBy(3, n);
var isDivisibleBy5 = isDivisibleBy(5, n);
if ( isDivisibleBy3 && isDivisibleBy5 )
return "FizzBuzz";
if ( isDivisibleBy3 )
return "Fizz";
if ( isDivisibleBy5 )
return "Buzz"
return n.ToString();
}
그리고 우리는 모든 합리적인 의견을 다루었지만, 불합리한 의견은 어떻습니까? 0 또는 음수를 전달하면 어떻게됩니까? 해당 테스트 사례를 작성하십시오.
public String FizzBuzz(int n) {
if (n < 1)
throw new InvalidArgException("n must be >= 1);
var isDivisibleBy3 = isDivisibleBy(3, n);
var isDivisibleBy5 = isDivisibleBy(5, n);
if ( isDivisibleBy3 && isDivisibleBy5 )
return "FizzBuzz";
if ( isDivisibleBy3 )
return "Fizz";
if ( isDivisibleBy5 )
return "Buzz"
return n.ToString();
}
아직 "실제 코드"처럼 보이기 시작합니까? 더 중요한 것은 어떤 시점에서 "언리얼 코드"가 중단되고 "실제"로 전환 되었습니까? 그것은 숙고할만한 것입니다 ...
그래서, 나는 각 단계에서 통과하지 못할 것이라는 것을 알았지 만 많은 연습을 해본 테스트를 찾아 간단하게 수행 할 수있었습니다. 내가 일할 때 상황이 그렇게 간단하지는 않으며 어떤 테스트가 변경을 강제 할 것인지 항상 알 수는 없습니다. 때로는 테스트를 작성하고 이미 통과 한 것을보고 놀랄 것입니다! 시작하기 전에 "테스트 목록"을 작성하는 습관을들이는 것이 좋습니다. 이 테스트 목록에는 생각할 수있는 모든 "흥미로운"입력이 포함되어야합니다. 모두 사용하지 않을 수 있으며 진행하면서 사례를 추가 할 수도 있지만이 목록은 로드맵 역할을합니다. FizzBuzz에 대한 내 테스트 목록은 다음과 같습니다.
- 부정
- 제로
- 하나
- 두
- 셋
- 네
- 다섯
- 6 개 (3의 비소수 배수)
- 아홉 (3 제곱)
- 10 (5의 사소한 배수)
- 15 (3과 5의 배수)
- 30 (3과 5의 사소한 배수)