1. TDD (BackEnd)

1.1. 소개

이 문서는 백엔드 프로그램의 산출물 중 하나인 TDD 작성방법에 대해 설명합니다.

1.2. 산출물에 TDD 코드가 포함 된 이유

비지니스 서비스 구현 시, 안정된 개발을 위해 많은 설계 문서가 필요합니다. 프로젝트 계획서, 요구사항 관리문서, 설게문서, 테스트 계획서, 품질관리 방안, 메뉴얼 등, 수행과정과 결과의 검증 또는 인수인계를 위한 산출물이 그 것입니다.

하지만, 이러한 프로젝트 산출물은 프로젝트 마다 각각 달라 매번 다른 환경에서의 작성이 부담이 되며, 때로는 프로젝트 진행에 부담이 되는 경우도 있습니다.

우리는 프로그램 개발의 수행과정과 결과의 검증에 관심이 많으며, 테스트 주도 개발(TDD)이 지켜지기를 원합니다. 때문에, 부담이 되는 여러 산출물을 대폭 줄이고, 테스트코드가 산출물의 기본이 되길 바랍니다.

1.3. TDD 작성 방법

TDD는 서비스 계층의 모든 객체와 함수, 그리고 호출관계를 대상으로 하며 이들 테스트를 위해 nUnit, .NET moq 프레임워크를 사용합니다.

1.3.1. 테스트 객체 만들기

테스트 객체는 프로젝트와 쌍을 이루는 테스트 프로젝트에 동일한 네임스페이스 및 폴더 구조로 작성합니다.

1.3.1.1. 테스트 객체 네이밍

테스트 객체의 이름은 테스트 대상 객체의 이름뒤에 Test 접미어를 가집니다. 여러 이름을 가져야 하는 테스트 클래스는 TestOf 특성을 사용하여 어떤 객체의 테스트인지 명시 하십시오.

namespace appTest.service.myService
{
  public class MyClassTests : TestUnit<MyClass> // TestUnit<T>은 다른장에서 설명합니다.
  {
    [Test]
    public void GwtMethodTest()
    {
      /* ... */
    }
  }

  [TestOf(Typeof(MyClass))]
  public class MyClassInstanceCreateTest
  {
    /* ... */
  }
}

1.3.1.2. 테스트 함수 네이밍

함수의 테스트 코드 작성은 보통 1:N의 관계로 테스트 코드가 더 많이 생성됩니다. 이 때, 함수의 네이밍을 기능적인 관점에서 생각하는게 무엇을 테스트 하려고 하는지 이해하는데 도움이 됩니다.

어떤 관점으로 테스트를 하려고 하는지 함수명에 표현이 된다면, 함수가 가져야 할 명확한 기능의 이해에 도움이 될 것입니다.

우리의 테스트 함수의 네이밍은 기본적으로 테스트 대상의 함수명에 이어서 GWT 구조를 갖습니다. Given / When / Then (GWT) 구조로 네이밍을 하여 테스트가 어떤 조건하에 어떠한 결과를 원하는지 기능적인 관점으로 접근 하도록 작성합니다.

[Test]
public void CreateInstance_파라메터가_A이면_A인스턴스를_반환한다()
{
  /* ... */
}

[Test]
public void CreateInstance_파라메터가_없으면_예외상황이된다()
{
  /* ... */
}

1.3.1.3. 테스트 함수 구성

앞서 GWT 구조의 네이밍으로 기능적인 관점의 테스트 함수가 작성되면, 이어서 테스트 코드를 채워 넣어야 합니다. 우리는 이 때, 고전적인 방식의 AAA 패턴으로 테스트 코드를 작성합니다. Arrange / Act / Assert (AAA) 구조로 테스트 코드를 작성하여 테스트 코드를 정리하고, 실행하고, 검증하십시오. 이러한 패턴의 테스트 코드 작성은 다른 테스트 코드들과의 일관성을 유지 합니다. 아래의 예제처럼 주석을 유지하여 주십시오.

[Test]
public void CreateInstance_파라메터가_A이면_A인스턴스를_반환한다()
{
  // Arrange
  var parameter = "A";

  // Act
  IMyClass myClass = Instance.CreateInstance(parameter);

  // Assert
  Assert.AreEquals(typeof(MyAclass), myClasss.GetType());
}

1.3.2. 테스트 객체 생성에 유용한 추상객체

nUnit, moq 를 이용한 테스트 코드 작성에는 선행되어야 할 많은 설정부분이 존재 합니다. 우리는 이러한 설정과 테스트 코드의 일관성을 위해 테스트 객체 생성에 유횽한 추상객체를 제공합니다.

1.3.2.1. TestUnit

TestUnit<ClassType> 객체는 단위테스트 작성 시, 필요한 선행 환경설정을 대신해주며, Mocking에 도움이 되는 함수를 가지고 있는 단위테스트 도움 객체 입니다. 제네릭 타입의 인스턴스를 테스트마다 매번 새로 생성하고, 필요한 환경설정을 자동으로 진행합니다. 우리는 GWT, AAA 패턴을 준수하며 테스트 코드에 집중할 수 있습니다.

TestUnit 설명
Instance 생성된 제네릭 타입의 인스턴스 입니다.
직접 인스턴스를 매번 생성할 필요 없이 이 속성에 접근하여 테스트 할 수 있습니다.
Repo 목킹(Mock)될 객체를 생성할 수 있는 MockRepository 속성 입니다.
모든 목킹은 이 속성으로 작성할 수 있습니다.
CallSeq 목킹된 메소드의 호출 순서가 저장되는 속성입니다.
호출 순서가 중요한 테스트에서 사용할 수 있습니다.
void MockAbstractOrInterfaceProperties() Instance의 속성들중 타입이 추상객체이거나 인터페이스인경우 모두 Repo를 이용하여 목킹합니다.
테스트 메소드에서 1회 직접 메소드를 호출해야 하며, 모든 테스트 코드에서 반복적인 경우 [Setup]에서 사용할 수도 있습니다.
object PrivateInvoke(string name, params object[] args) Instance의 Private 함수를 테스트 할 수 있는 함수 입니다.
Private 함수 이름과, 전달할 파라메터들을 입력합니다. 테스트 함수가 반환형인경우 값을 반환합니다.

1.3.2.2. TestMock

TestMock<AbstractOrInterfaceType> 객체는 인터페이스 또는 추상계층의 객체의 테스트를 지원하기 위한 도움 객체 입니다. 제네릭 타입의 목킹된 객체를 테스트마다 매번 자동으로 생성하고, 필요한 환경설정을 자동으로 진행합니다. 보통 추상계층의 함수의 호출 순서, 여부 등을 검증할 때 사용합니다.

TestMock 설명
obj 목킹된 제네릭 타입의 인스턴스 입니다.
직접 인스턴스를 매번 생성할 필요 없이 이 속성에 접근하여 테스트 할 수 있습니다.
repo 목킹(Mock)될 객체를 생성할 수 있는 MockRepository 속성 입니다.
모든 목킹은 이 속성으로 작성할 수 있습니다.
mock 제네릭 타입의 Mock 객체 입니다.
이 속성으로 Mock 설정을 작성할 수 있습니다.
CallSeq 목킹된 메소드의 호출 순서가 저장되는 속성입니다.
호출 순서가 중요한 테스트에서 사용할 수 있습니다.
object PrivateInvoke(string name, params object[] args) Instance의 Private 함수를 테스트 할 수 있는 함수 입니다.
Private 함수 이름과, 전달할 파라메터들을 입력합니다. 테스트 함수가 반환형인경우 값을 반환합니다.

1.3.2.3. TestWithSpring

간혹 테스트 범위가 커질 때가 있습니다. 혹은 Database의 동작까지 테스트가 필요할 때가 있습니다. 이럴 땐, 테스트 대상 객체에서 사용하는 DAO나 다른 속성의 실제 인스턴스를 생성하여 할당할 필요가 있습니다. 하지만 Spring.NET의 IoC, DI에 의존하는 프로그램은 테스트에 앞서 이런 초기화 작업에 많은 어려움이 생깁니다.

TestWithSpring 객체는 이럴 때, 사용합니다. 웹서비스를 통해 동작할 때를 가정하여 테스트 할 수 있습니다.

TestWithSpring 설명
void SetTimeout(int sec = 60) 데이터베이스
repo 목킹(Mock)될 객체를 생성할 수 있는 MockRepository 속성 입니다.
모든 목킹은 이 속성으로 작성할 수 있습니다.
mock 제네릭 타입의 Mock 객체 입니다.
이 속성으로 Mock 설정을 작성할 수 있습니다.
CallSeq 목킹된 메소드의 호출 순서가 저장되는 속성입니다.
호출 순서가 중요한 테스트에서 사용할 수 있습니다.
object PrivateInvoke(string name, params object[] args) Instance의 Private 함수를 테스트 할 수 있는 함수 입니다.
Private 함수 이름과, 전달할 파라메터들을 입력합니다. 테스트 함수가 반환형인경우 값을 반환합니다.

1.3.2.4. 테스트 코드 작성 주의 사항

Private (비공개) 멤머를 테스트 해야 하는 것은 잘못된 코드 디자인의 징후 입니다. 하지만, 비공개 멤버를 테스트 한다고 해서 반드시 잘못되었다는 것은 아닙니다. 비공개 멤버나 함수는 단위테스트에서 중요하기 때문입니다. 보통 Privaet(비공개) 멤버는 구현 객체의 간단한 세부사항임으로 대상 객체의 주요 기능에 영향을 주지 않아야 합니다. 대상 객체의 주요기능에 영향을 줄 정도의 많은 비공개 테스트나, 많은 일을하는 Private(비공개) 멤버를 테스트 하고 있다면, 한번쯤 잘못된 설계를 의심해 보아야 합니다.

1.3.3. 테스트 코드 예제

예제로써 주로 발생하는 개발 상황을 가정하고 작성 된, 테스트 코드를 설명합니다.

1.3.3.1. 단위테스트 코드 예제

모듈화가 잘 된 설계는 기능적인 관점으로 단위테스트 코드를 작성할 수 있습니다.

public class