Search

'테스트 주도 개발'에 해당되는 글 2건

  1. 2009.06.29 [Testing] Moq.NET (T/B Driven Development)
  2. 2009.06.29 [Testing] TDD (Test-Driven Development-테스트 주도 개발)

[Testing] Moq.NET (T/B Driven Development)

Agile Development 2009. 6. 29. 12:36 Posted by POWERUMC

 

Moq.NET

Moq 는 "Mock-you" 또는 "Mock" 로 부른다고 합니다. Moq.NET 3.0 은 C# 3.0 과 .NET Framework 3.5 를 통해 Linq Expression Tree 와 Lambda Expression(람대 표현식) 으로 직관적이고 생산적이라고 합니다.

이전에 봤던 웹 사이트 로그인 사용자 스토리를 다시 봅시다. 단, 이 예제에서는 복잡성을 만족하는 항목을 삭제합니다.

웹 사이트의 로그인 사용자 스토리

  • 사용자 아이디는 영문만 입력 가능하고 한글은 입력할 수 없다
  • 사용자 아이디는 최소 3자리, 최대 10자리까지 입력가능하고 초과시 경고 메시지를 보여준다
  • 사용자 비밀번호는 최소 5자리, 최대 20자리까지 입력 가능하고 초과시 경고 메시지를 보여준다

   

위의 사용자 스토리를 Mocking Framework 인 Moq.NET 을 이용하여 TDD와 BDD 형태로 테스트를 작성해 나갈 수 있습니다.

아래의 소스 코드는 Login 인터페이스를 정의한 코드입니다.

public interface ILogin
{

LoginResult Login(string id, string password);

LoginInputResult Valide(string id, string password);

}

   

public enum LoginResult

{

Authentication,

NoAuthentication

}

   

public enum LoginInputResult

{

Success,

MinLengthId,

MaxLengthId,

MinLengthPassword,

MaxLengthPassword

}

   

이제 Login 의 Behavior(행위) 측면에서 주도적인 개발을 하고자 합니다. BDD 이용하여 [그림1] 의 Clean up code 를 얻기 위한 목적이 아닙니다. 좀 더 TDD 에 가깝고, 디자인과 설계에 초점을 맞추고자 합니다.

class Program
{

static void Main(string[] args)

{

var id = "powerumc";

var pwd = "aaaa";

var mock = new Mock<ILogin>();

}

   

private static string minString(int length)

{

return Match<string>.Create( o => o.Length <= length );

}

   

private static string maxString(int length)

{

return Match<string>.Create( o => o.Length >= length );

}

}

   

위의 코드를 통해 Mocking Object 를 생성하도록 Mock<T> 생성자를 볼 수 있습니다. 바로 Mock<T> 를 통해 Mocking Object 를 생성합니다.

아래의 코드는 위의 로그인 사용자 스토리에서 입력되는 아이디/비밀번호의 유효성을 검사하도록 Mocking Object 를 설정합니다.

// Validate

mock.Setup( o => o.Valide(minString(3), It.IsAny<string>()))

.Returns(LoginInputResult.MinLengthId)

.Callback(()=>Console.WriteLine("ERROR> MinLengthId"));

mock.Setup( o => o.Valide(maxString(10), It.IsAny<string>()))

.Returns(LoginInputResult.MaxLengthId)

.Callback(()=>Console.WriteLine("ERROR> MaxLengthId"));

mock.Setup( o => o.Valide(It.IsAny<string>(), minString(3)))

.Returns(LoginInputResult.MinLengthPassword)

.Callback(()=>Console.WriteLine("ERROR> MinLengthPassword"));

mock.Setup( o => o.Valide(It.IsAny<string>(), maxString(20)))

.Returns(LoginInputResult.MaxLengthPassword)

.Callback(()=>Console.WriteLine("ERROR> MaxLengthPassword"));

   

아래의 코드는 유효성을 통과한 아이디/비밀번호로 로그인을 시도하고 결과값을 리턴하는 Mocking Object 입니다.

// Login

mock.Setup( o => o.Login(It.IsAny<string>(), It.IsAny<string>()))

.Returns(LoginResult.NoAuthentication)

.Callback(()=>Console.WriteLine("No Authentication"));

mock.Setup( o => o.Login(id, pwd))

.Returns(LoginResult.Authentication)

.Callback(()=>Console.WriteLine("Success Login"));

   

자, 그럼 가상의 Mocking Object 를 통해 인터페이스만으로 로그인 시도를 해보도록 하겠습니다.

var obj = mock.Object;

var loginInputResult = obj.Valide(id,pwd);

   

if( loginInputResult != LoginInputResult.Success ) return;

   

obj.Login(id,pwd);

   

입력 변수 값에 따라 비록 Mocking Object 지만, 테스트 시나리오를 충분히 검증할 수 있습니다.

Id="aaa", pwd="aaa" 일 경우는 아래와 같은 결과가 나타나겠죠~?

 

Id="powerumc", pwd="aaaa" 일 경우는 아래와 같이 모든 테스트 시나리오를 통과한 결과입니다.

 

그럼, Microsoft Research 프로젝트의 일환인 Pex 를 이용하여 Mocking Object 를 Testing 해보겠습니다. Pex 테스트 프로젝트를 생성하고 아래의 PexMethod 를 만들었습니다.

아래는 테스트 결과입니다. 총 31개의 테스트 케이스 중에 통과되는 1개의 테스트를 확인할 수 있습니다.

자 어떻습니다~? 전혀 구현 코드를 구현하지 않았음에도 인터페이스를 통해 TDD-테스트 주도 개발이 가능합니다. BDD-행위 주도 개발을 통해 번거로운 TDD 의 Red, Green, Refector 의 유한반복 사이클 없이도, 실제 인터페이스의 디자인과 설계에 초점을 맞추어 테스트를 진행하였습니다.

단순히 BDD 가 최고야~ 라는 말은 아닙니다. 우리가 BDD 를 통해 TDD 를 좀 더 쉽고, 근접하게 접근할 수 있습니다. 그리고 코드의 구현이 전혀 없이 오직 디자인과 설계에 초점이 맞추어져 있다는 사실을 알면 됩니다. 바로 Moq.NET 은 Mocking Object 를 통해 TDD 와 BDD 개발을 통해 좀 더 품질 좋은 WhiteBox Testing 의 기대효과를 누릴 수 있습니다.

데이터베이스의 데이터를 조회하여 테스트한다고 할 때에도, 반드시 데이터베이스가 존재하지 않아도 됩니다. 가상의 데이터베이스 커넥션 객체를 만들어서 쓰면 됩니다. 쇼핑몰 사이트에서 결재 프로세스를 테스트해야 하나요? 그렇다면 가상의 객체를 통해 설계된 결재 프로세스가 합당한지 Mocking Object 를 통해 테스트를 하면 됩니다. 그 이후에는 잘 설계된 인터페이스를 구현하기만 하면 되겠죠?

   

이 외에도 WikiPedia 에 Mock Library 의 종류가 있네요. 아무튼 테스트와 Moq.NET 에 대한 내용은 여기까지 하는 것으로 마치도록 하렵니다.

 

참고 문헌

http://en.wikipedia.org/wiki/Mock-object
http://behaviour-driven.org/

이번에 Moq.NET 3.0 버전이 릴리즈 되었습니다. Moq.NET 는 Mocking Object 를 통해 특정 테스트를 진행하고 훨씬 TDD 기반에 근접한 테스팅을 가능하게 합니다. 즉, Mocking Object 는 실제 클래스나 개발이 완료되지 않는 시점에서부터 테스트를 가능하도록 합니다.

그런데 필자는 Moq.NET 를 이해하는 과정에서 내가 알고 있던 것보다 더 깊은 배경이 있었다는 것을 알게 되었습니다. 예를 들어, TDD 외에 BDD 또한 애자일 개발 방법론에 포함된다는 사실과, 조금은 낯설은 BDD-행위 주도 개발을 직접 체험해 보는 과정에서 말입니다. ^^ 

   

왜 TDD (Test-Driven Development) ?

TDD 가 좋으면서 쓰지 않는 이유는 뭘까요? 일반적으로 완벽한 TDD 를 수행하는 과정은 매우 힘듭니다. TDD 에 대한 이론을 들었을때는 확 가슴에 와닿지만, 이것을 몸소 체험하는 과정에서 개발자의 인내력의 한계를 올려놨다 내려놨다 하는 극한을 체험하게 합니다^^; 그러므로 일반적인 TDD 사이클인 Red, Green, Refactor 는 사람을 금방 지치게 만들죠^^; 한 사이클을 마치기 위해서는 많은 시간이 투자되어야 한다는 겁니다.

[그림1] TDD Process

하지만 우리가 TDD 를 수행하는 목적은 이러한 과정에서 코드에 대한 신뢰도를 향상시키고 품질을 향상시키고자 하는 것입니다. 그렇기 때문에 하고자 하는 목적이 보다 나은 품질을 보장하기 위해서라면 TDD 가 필요하다는 것이 머릿속으로만 느낌이 팍팍 옵니다^^;

이제 슬슬 스스로에게 딜레마다 옵니다. 좋은 줄은 알지만 누군가 나에게 TDD 를 강요한다면 아마도 전 "Oh~ No!" 라고 할 것 같네요 -_-;

 

WhiteBox Testing & BlackBox Testing

Moq 를 이야기 하기도 전에 정말 딴소리를 많이 하네요. 일반적으로 테스팅은 크게 WhiteBox Testing 과 BlackBox Testing 으로 구분할 수 있습니다. 이 두 가지 테스팅의 차이는 좁은 범위에서 내부적인 프로세스를 아느냐 모르느냐의 차이고요, 넓은 범위에서는 테스트 레벨의 차이라고 보시면 됩니다.

다음의 그림을 보면 좀 더 이해가 쉬울 겁니다.

[그림2] BlackBox Test 와 WhiteBox Test

즉 BlackBox Test 와 WhiteBox 테스트는 그 목표의 설정이 다르게 됩니다.

BlackBox Test 는 로그인 기능에 대해 요구사항이 있고, 그 기능이 반드시 가져야 할 스팩이 있을 것입니다. 요구사항을 통과하기 위해서는 로그인 기능의 스펙을 만족하면 되고, 실제 단위 테스트 등으로 그런 케이스를 통과를 하면 됩니다. 즉, 내부적으로 어떻게 동작하는지는 알아야 할 필요가 없으므로 수십/수백가지의 테스트 케이스를 통과하여 기능이 정상적으로 동작하는 것을 보장하기 위한 목표입니다.

WhiteBox Test 는 해당 기능에 대해 내부적인 구조를 기반으로 테스트를 진행하게 됩니다. 테스터는 이미 위의 로그인 기능이 내부적으로 어떤 프로세스로 진행되는지 알고 있으며, 예측 가능한 테스트 시나리오를 작성하여 테스트를 진행하게 됩니다. 이 테스트의 목적은 테스트 케이스를 통과하는 BlackBox Test 를 한 단계 뛰어넘어 잠재적인 오류까지 테스트를 통해 잡아내는 것이 목표입니다.
즉, 코드상의 Memory Leak 이나 SQL Injection, Stack Overflow 등 잠재적인 오류를 미리 찾는 경우도 있습니다. 이미 이런 기능은 Visual Studio 2005 이상부터 지원되는 정적 코드 분석(Static Code Analysis) 가 제공이 됩니다. 또, WhiteBox Test 는 코드 커버리지(Code Coverage) 의 수치를 높임으로써 테스트가 안된 코드의 양을 최소화 시키고, 궁극적으로 소프트웨어의 품질을 향상시키는데 있습니다.(Software QA)