간단한 기능을 코딩하기 위해 TDD를 사용하여 시작하려면 어떻게해야합니까?


9

나는 기본적으로 TDD의 요지가 있습니다. 나는 그것이 유용하다는 것을 팔았으며 MSTEST 프레임 워크에 대한 합리적인 명령을 받았습니다. 그러나 지금까지 나는 그것을 주요 개발 방법으로 사용하는 것을 졸업 할 수 없었습니다. 주로 콘솔 앱을 테스트 드라이버로 작성하는 대리자로 사용합니다 (전통적인 접근 방식).

나에게 가장 유용한 것은 회귀 테스트의 역할을 흡수하는 방식입니다.

나는 아직 테스트 할 수없는 다양한 행동을 구체적으로 격리시키는 것을 아직 구축하지 않았다.

따라서이 질문은 다음 개발 작업을 위해 작성할 수있는 첫 번째 테스트에 대한 포인터를 요청하는 것입니다. 생산자 / 소비자의 방식으로 작업 실행을 캡슐화하는 코드를 만들고 싶습니다.

나는이 코드를 작성한 후에이 질문을 작성하기로 결정했습니다 (실제로 TDD를 실제로 사용할 수 있는지 궁금합니다)

암호:

interface ITask
{
    Guid TaskId { get; }
    bool IsComplete { get; }
    bool IsFailed { get; }
    bool IsRunning { get; }
}

interface ITaskContainer
{
    Guid AddTask(ICommand action);
}

interface ICommand
{
    string CommandName { get; }
    Dictionary<string, object> Parameters { get; }
    void Execute();
}

먼저 테스트를 작성하고 인터페이스를 작성해야합니다! 전체 아이디어는 TDD가 API를위한 것입니다.

1
아직 존재하지 않는 인터페이스에 대한 테스트는 어떻게 작성합니까? 그들은 컴파일조차하지 않습니다.
Robert Harvey

5
그것은 첫 번째 실패한 시험입니다.
cori

답변:


10

이 개념부터
시작하십시오 . 1) 원하는 행동으로 시작하십시오. 테스트를 작성하십시오. 테스트 실패를 참조하십시오.
2) 시험에 합격하기에 충분한 코드를 작성하십시오. 모든 테스트 통과를 참조하십시오.
3) 중복 / 조잡한 코드-> 리 팩터를 찾으십시오. 테스트가 여전히 통과 참조하십시오. 고토 1

따라서 # 1에서 새로운 명령을 만들고 싶다고 가정 해 봅시다 (명령이 작동하는 방식으로 확장 중이므로 나에게 견딜 것입니다). (또한 극단적 인 TDD보다는 약간 실용적입니다)

새 명령의 이름은 MakeMyLunch이므로 먼저 인스턴스화 할 테스트를 작성하고 명령 이름을 가져옵니다.

@Test
public void instantiateMakeMyLunch() {
   ICommand command = new MakeMyLunchCommand();
   assertEquals("makeMyLunch",command.getCommandName());
}

새 명령 클래스를 작성하고 이름을 리턴하도록 강제하지는 않습니다 (순수자는 이것이 1이 아닌 2 라운드의 TDD라고 말합니다). 따라서 클래스를 작성하고 명령 이름 리턴을 포함하여 ICommand 인터페이스를 구현하도록하십시오. 모든 테스트를 실행하면 이제 모든 패스가 표시되므로 리팩토링 기회를 찾으십시오. 아마 없습니다.

다음으로 실행을 구현하고 싶습니다. "MakeMyLunch"가 성공적으로 "내 점심을 만들었다"는 것을 어떻게 알 수 있습니까? 이 작업으로 인해 시스템에 어떤 변화가 있습니까? 이것을 테스트 할 수 있습니까?

테스트하기가 쉽다고 가정하십시오.

@Test
public void checkThatMakeMyLunchIsSuccessful() {
   ICommand command = new MakeMyLunchCommand();
   command.execute();
   assertTrue( Lunch.isReady() );
}

다른 경우에는 이것이 더 어렵고, 실제로하고 싶은 것은 테스트 대상 (MakeMyLunchCommand)의 책임을 테스트하는 것입니다. 아마도 MakeMyLunchCommand의 책임은 Fridge 및 Microwave와 상호 작용하는 것입니다. 그래서 그것을 테스트하기 위해 mock Fridge와 mock Microwave를 사용할 수 있습니다. [두 샘플 모의 프레임 워크는 MockitonMock 이거나 여기를보십시오 .]

이 경우 다음 의사 코드와 같은 작업을 수행합니다.

@Test
public void checkThatMakeMyLunchIsSuccessful() {
   Fridge mockFridge = mock(Fridge);
   Microwave mockMicrowave = mock(Microwave);
   ICommand command = new MakeMyLunchCommand( mockFridge, mockMicrowave );
   command.execute();
   mockFramework.assertCalled( mockFridge.removeFood );
   mockFramework.assertCalled( microwave.turnon );
}

순수 주의자는 수업의 책임을 테스트합니다-다른 수업과의 상호 작용 (명령이 냉장고를 열고 전자 레인지를 켰습니까?)

실용 주의자는 수업 그룹을 테스트하고 결과를 테스트한다고 말합니다 (점심 준비가 되셨습니까?).

귀하의 시스템에 적합한 저울을 찾으십시오.

(참고 : 인터페이스 구조가 너무 일찍 도착했다는 것을 고려하십시오. 단위 테스트 및 구현을 작성하면서 3 단계에서 공통 인터페이스 기회를 "통지"할 수 있습니다.


인터페이스를 미리 작성하지 않은 경우 어떤 질문으로 인해 Execute () 메서드가 만들어졌습니다. 추가 기능을 자극하는 "단계"가 없을 때 TDD에 대한 초기 시도 중 일부가 중단되었습니다. 옆으로
밟아야

1
좋은 질문! 사용자가 만든 유일한 명령이 "MakeMyLunchCommand"인 경우이 메소드는 ".makeMyLunch ()"로 시작했을 수 있습니다. 어느 것이 좋았을까요. 그런 다음 다른 명령 ( "NapCommand.takeNap ()")을 만듭니다. 다른 방법으로는 여전히 문제가 없습니다. 그런 다음 생태계에서이를 사용하기 시작합니다. ICommand 인터페이스로 일반화해야 할 수도 있습니다. YAGNI [ en.wikipedia.org/wiki/You_ain't_gonna_need_it ] =) 다른 경우에, 당신은 그것을 시작하기 시작한다는 것을 알고 있기 때문에 일반적으로, 마지막 책임있는 순간까지 일반화를 지연 시킵니다.
jayraynet

(여기서는 메소드 이름과 같은 리팩토링을 만드는 최신 IDE를 사용한다고 가정합니다)
jayraynet

1
충고에 다시 한 번 감사드립니다. 마침내 모든 조각과 그 구성이 어떤지 알 수있게되었습니다. 예, 도구 상자에 빠른 리 팩터가 있습니다
Aaron Anodide
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.