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"; }
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/
'Agile Development' 카테고리의 다른 글
애자일에 대한 고찰 (3) | 2010.03.15 |
---|---|
[Better Code]Visual Studio Code Analysis Enhancements - 3. Data Flow Rules and Phoenix Engine (0) | 2009.12.10 |
[Testing] BDD (Behavior-Driven Development–행위 주도 개발) (0) | 2009.06.29 |
[Testing] TDD (Test-Driven Development-테스트 주도 개발) (0) | 2009.06.29 |
[Better Code]Visualize Code Relationships (0) | 2009.05.02 |