C # 및 RhinoMocks를 사용한 테스트 주도 개발의 모범 사례 [닫기]


87

우리 팀이 테스트 가능한 코드를 작성하는 데 도움이되도록 C # 코드 기반을보다 테스트 가능하게 만들기위한이 간단한 모범 사례 목록을 만들었습니다. (일부 요점은 C # 용 모의 프레임 워크 인 Rhino Mocks의 제한 사항을 참조하지만 규칙이 더 일반적으로 적용될 수도 있습니다.) 누구든지 자신이 따르는 모범 사례가 있습니까?

코드의 테스트 가능성을 최대화하려면 다음 규칙을 따르십시오.

  1. 먼저 테스트를 작성한 다음 코드를 작성하십시오. 이유 : 이렇게하면 테스트 가능한 코드를 작성하고 모든 코드 줄에 테스트가 작성됩니다.

  2. 종속성 주입을 사용하여 클래스를 디자인합니다. 이유 : 보이지 않는 것을 조롱하거나 테스트 할 수 없습니다.

  3. Model-View-Controller 또는 Model-View-Presenter를 사용하여 UI 코드를 동작과 분리합니다. 이유 : 테스트 할 수없는 부분 (UI)을 최소화하면서 비즈니스 논리를 테스트 할 수 있습니다.

  4. 정적 메서드 나 클래스를 작성하지 마십시오. 이유 : 정적 메서드는 분리하기 어렵거나 불가능하며 Rhino Mocks는이를 모의 할 수 없습니다.

  5. 클래스가 아닌 인터페이스를 프로그래밍하십시오. 이유 : 인터페이스를 사용하면 개체 간의 관계가 명확 해집니다. 인터페이스는 객체가 환경에서 필요로하는 서비스를 정의해야합니다. 또한 Rhino Mocks 및 기타 모의 프레임 워크를 사용하여 인터페이스를 쉽게 모의 할 수 있습니다.

  6. 외부 종속성을 분리하십시오. 이유 : 해결되지 않은 외부 종속성을 테스트 할 수 없습니다.

  7. 모의하려는 방법을 가상으로 표시하십시오. 이유 : Rhino Mocks는 가상이 아닌 방법을 모의 할 수 없습니다.


이것은 유용한 목록입니다. 우리는 현재 NUnit과 Rhino.Mocks를 사용하고 있으며, 단위 테스트의 측면에 익숙하지 않은 팀원을 위해 이러한 기준을 설명하는 것이 좋습니다.
Chris Ballard

답변:


58

확실히 좋은 목록입니다. 여기에 대한 몇 가지 생각이 있습니다.

먼저 테스트를 작성한 다음 코드를 작성하십시오.

나는 높은 수준에서 동의합니다. 하지만 좀 더 구체적으로 설명하겠습니다. "먼저 테스트를 작성한 다음 테스트를 통과하기에 충분한 코드를 작성 하고 반복하십시오." 그렇지 않으면 내 단위 테스트가 통합 또는 수용 테스트와 비슷해 보일까 두렵습니다.

종속성 주입을 사용하여 클래스를 디자인합니다.

동의합니다. 개체가 자체 종속성을 만들면 사용자는이를 제어 할 수 없습니다. Inversion of Control / Dependency Injection은 해당 제어를 제공하여 모의 / 스텁 / 등을 사용하여 테스트중인 개체를 격리 할 수 ​​있습니다. 이것은 개체를 격리하여 테스트하는 방법입니다.

Model-View-Controller 또는 Model-View-Presenter를 사용하여 UI 코드를 동작과 분리합니다.

동의합니다. 발표자 / 컨트롤러조차도 스텁 / 모의보기 및 모델을 전달하여 DI / IoC를 사용하여 테스트 할 수 있습니다. 자세한 내용은 Presenter First TDD를 확인하십시오 .

정적 메서드 나 클래스를 작성하지 마십시오.

이것에 동의하는지 잘 모르겠습니다. 모의를 사용하지 않고 정적 메서드 / 클래스를 단위 테스트 할 수 있습니다. 아마도 이것은 당신이 언급 한 Rhino Mock 특정 규칙 중 하나 일 것입니다.

클래스가 아닌 인터페이스를 프로그래밍하십시오.

동의하지만 약간 다른 이유가 있습니다. 인터페이스는 소프트웨어 개발자에게 다양한 모의 객체 프레임 워크를 지원하는 것 외에도 상당한 유연성을 제공합니다. 예를 들어, 인터페이스 없이는 DI를 제대로 지원할 수 없습니다.

외부 종속성을 분리하십시오.

동의합니다. 인터페이스를 사용하여 자신의 파사드 또는 어댑터 (적절한 경우) 뒤에 외부 종속성을 숨 깁니다. 이를 통해 웹 서비스, 대기열, 데이터베이스 등 외부 종속성으로부터 소프트웨어를 격리 할 수 ​​있습니다. 이는 팀이 종속성 (외부라고도 함)을 제어하지 않을 때 특히 중요합니다.

모의하려는 방법을 가상으로 표시하십시오.

이것이 Rhino Mocks의 한계입니다. 모의 객체 프레임 워크보다 손으로 코딩 된 스텁을 선호하는 환경에서는 필요하지 않습니다.

그리고 고려해야 할 몇 가지 새로운 사항 :

창의적인 디자인 패턴을 사용하십시오. 이는 DI에 도움이되지만 해당 코드를 격리하고 다른 로직과 독립적으로 테스트 할 수도 있습니다.

Bill Wake의 Arrange / Act / Assert 기술을 사용하여 테스트를 작성합니다 . 이 기술은 필요한 구성, 실제로 테스트중인 항목 및 예상되는 항목을 매우 명확하게합니다.

자신의 모크 / 스텁을 굴리는 것을 두려워하지 마십시오. 종종 모의 개체 프레임 워크를 사용하면 테스트를 읽기가 매우 어려워집니다. 직접 롤링하면 모의 / 스텁을 완벽하게 제어 할 수 있으며 테스트를 읽기 쉽게 유지할 수 있습니다. (이전 포인트를 다시 참조하십시오.)

단위 테스트의 중복을 추상 기본 클래스 또는 설정 / 해체 방법으로 리팩터링하려는 유혹을 피하십시오. 그렇게하면 단위 테스트를 시도하는 개발자로부터 구성 / 정리 코드가 숨겨집니다. 이 경우 중복을 리팩토링하는 것보다 각 개별 테스트의 명확성이 더 중요합니다.

지속적인 통합을 구현합니다. 모든 "녹색 막대"에서 코드를 체크인하십시오. 체크인 할 때마다 소프트웨어를 빌드하고 전체 단위 테스트를 실행하세요. (물론, 이것은 그 자체로 코딩 관행은 아니지만 소프트웨어를 깨끗하고 완전히 통합 된 상태로 유지하기위한 놀라운 도구입니다.)


3
일반적으로 테스트를 읽기 어려운 경우 프레임 워크의 결함이 아니라 테스트중인 코드의 결함이라는 것을 알 수 있습니다. SUT를 설정하기가 복잡하다면 더 많은 개념으로 나누어야 할 것입니다.
Steve Freeman

10

.Net 3.5로 작업하는 경우 Moq 모킹 라이브러리 를 살펴볼 수 있습니다.이 라이브러리는 대부분의 다른 모의 라이브러리에서 직관적이지 않은 레코드 응답 관용구를 제거하기 위해 표현식 트리와 람다를 사용합니다.

빠른 시작 을 확인하여 테스트 사례가 얼마나 더 직관적 이되었는지 확인 하십시오. 다음은 간단한 예입니다.

// ShouldExpectMethodCallWithVariable
int value = 5;
var mock = new Mock<IFoo>();

mock.Expect(x => x.Duplicate(value)).Returns(() => value * 2);

Assert.AreEqual(value * 2, mock.Object.Duplicate(value));

5
Rhino Mocks의 새 버전도 이와 같이 작동한다고 생각합니다
George Mauer


3

이것은 매우 유용한 게시물입니다!

컨텍스트 및 테스트 대상 시스템 (SUT)을 이해하는 것이 항상 중요하다고 덧붙입니다. 기존 코드가 동일한 원칙을 따르는 환경에서 새 코드를 작성할 때 편지에 TDD 원칙을 따르는 것이 훨씬 쉽습니다. 그러나 TDD가 아닌 레거시 환경에서 새 코드를 작성할 때 TDD 노력이 예상치와 기대치를 훨씬 뛰어 넘을 수 있다는 것을 알게됩니다.

전적으로 학문적 인 세계에 살고있는 일부 사용자에게는 일정과 제공이 중요하지 않을 수 있지만 소프트웨어가 돈인 환경에서는 TDD 노력을 효과적으로 사용하는 것이 중요합니다.

TDD는 한계 수익 체감 법칙의 적용을받습니다 . 요컨대, TDD에 대한 귀하의 노력은 최대 수익 지점에 도달 할 때까지 점점 더 가치가 있으며, 그 후 TDD에 투자 된 시간은 점점 더 가치가 낮아집니다.

나는 TDD의 주요 가치가 경계 (블랙 박스)와 시스템의 미션 크리티컬 영역에 대한 가끔 화이트 박스 테스트에 있다고 믿는 경향이 있습니다.


2

인터페이스에 대한 프로그래밍의 진정한 이유는 Rhino의 삶을 더 쉽게 만드는 것이 아니라 코드에서 개체 간의 관계를 명확히하기 위해서입니다. 인터페이스는 객체가 환경에서 필요로하는 서비스를 정의해야합니다. 클래스는 해당 서비스의 특정 구현을 제공합니다. 역할, 책임 및 공동 작업자에 대한 Rebecca Wirfs-Brock의 "Object Design"책을 읽어보십시오.


동의합니다 ... 나는 그것을 반영하기 위해 내 질문을 업데이트 할 것입니다.
Kevin Albrecht

1

좋은 목록. 설정하고 싶은 것 중 하나는 클래스가 다른 라이브러리, 네임 스페이스, 중첩 네임 스페이스에 있어야하는 경우입니다. 사전에 라이브러리 및 네임 스페이스 목록을 파악하고 팀이 만나서 두 개를 병합 / 새로 추가하기로 결정해야한다고 명령 할 수도 있습니다.

오, 당신도 원할만한 제가하는 일을 생각했습니다. 일반적으로 각 테스트가 해당 네임 스페이스에 들어가는 클래스 정책 당 테스트 픽스처가있는 단위 테스트 라이브러리가 있습니다. 나는 또한 BDD 스타일의 다른 테스트 라이브러리 (통합 테스트?)를 갖는 경향이 있습니다 . 이를 통해 메서드가 수행해야하는 작업과 애플리케이션이 전체적으로 수행해야하는 작업을 지정하는 테스트를 작성할 수 있습니다.


또한 개인 프로젝트에서 유사한 BDD 스타일 테스트 섹션 (단위 테스트 코드 추가)을 수행합니다.
Kevin Albrecht

0

내가하고 싶은 또 다른 것이 있습니다.

TestDriven.Net 또는 NAnt가 아닌 단위 테스트 Gui에서 테스트를 실행하려는 경우 단위 테스트 프로젝트 유형을 라이브러리가 아닌 콘솔 애플리케이션으로 설정하는 것이 더 쉽다는 것을 알았습니다. 이를 통해 테스트를 수동으로 실행하고 디버그 모드에서 단계별로 진행할 수 있습니다 (앞서 언급 한 TestDriven.Net이 실제로 수행 할 수있는 작업).

또한 익숙하지 않은 코드와 아이디어를 테스트 할 수있는 Playground 프로젝트를 항상 열고 싶습니다. 이것은 소스 제어에 체크인해서는 안됩니다. 더 좋은 점은 개발자 컴퓨터에서만 별도의 소스 제어 저장소에 있어야한다는 것입니다.

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