Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Manning.The.Art.of.Unit.Testing.with.Examples.in.dot.NET.Jun.2009.pdf
Скачиваний:
18
Добавлен:
21.03.2016
Размер:
9.67 Mб
Скачать

102

CHAPTER 5

Isolation (mock object) frameworks

want it to return a specific value based on the parameters it receives, or to remember all the values for all the method calls on the same method (the parameter history)? The code gets ugly fast.

Using an isolation framework, the code for doing this becomes trivial, readable, and much shorter, as you’ll see when you create your first dynamic mock object.

NOTE The word fake was introduced in chapter 4 as a generic term for either a stub or a mock.

5.2 Dynamically creating a fake object

Let’s define dynamic fake objects, and how they’re different from regular mocks.

DEFINITION A dynamic fake object is any stub or mock that’s created at runtime without needing to use a handwritten implementation of an interface or subclass.

Using dynamic fakes removes the need to hand-code classes that implement interfaces or that derive from other classes, because this can be generated for the developer at runtime by a simple line of code.

5.2.1Introducing Rhino Mocks into your tests

In this chapter, we’ll use Rhino Mocks, an isolation framework that’s open source and freely downloadable from http://ayende.com. It’s simple and quick to use, with little overhead in learning how to use the API. I’ll walk you through a few examples, and then we’ll explore other frameworks and discuss the differences among them.

The only thing you’ll need to do to use Rhino Mocks, after downloading and unzipping it, (assuming you have NUnit installed on your machine) is to add a reference to the Rhino.Mocks.Dll. In the Add Reference dialog of the test project, click Browse, and locate the downloaded DLL file (which you can get from http://www.ayende.com/proj- ects/rhino-mocks/downloads.aspx).

Rhino Mocks allows us to create and use fakes in two different ways. The first one is the record-and-replay model, and is the one you’ll be see-

Dynamically creating a fake object

103

ing most in this chapter. The other is the arrange-act-assert model, which I’ll be discussing near the end of this chapter.

Rhino Mocks contains in its API a class called MockRepository that has special methods for creating mocks and stubs. The first one we’ll look at is StrictMock(). The method is called with a generic parameter matching the type of an interface or class that you’d like to fake, and it dynamically creates a fake object that adheres to that interface at runtime. You don’t need to implement that new object in real code.

5.2.2Replacing a handwritten mock object with a dynamic one

As an example, let’s look at a handwritten mock object used to check whether a call to the log was performed correctly. Listing 5.2 shows the test class and the handwritten mock.

Listing 5.2 Asserting against a handwritten mock object

[TestFixture]

public class LogAnalyzerTests

{

[Test]

public void Analyze_TooShortFileName_CallsWebService()

{

ManualMockService mockService = new ManualMockService ();

LogAnalyzer log = new LogAnalyzer(mockService); string tooShortFileName="abc.ext"; log.Analyze(tooShortFileName);

Assert.AreEqual("Filename too short:abc.ext", mockService.LastError);

}

}

public class ManualMockService:IWebService

{

public string LastError;

public void LogError(string message)

{

LastError = message;

}

}

? Creates dynamic mock object

104

CHAPTER 5

Isolation (mock object) frameworks

The parts of the code in bold are the parts that will change when we start using dynamic mock objects.

We’ll now create a dynamic mock object, and eventually replace the earlier test. Listing 5.3 shows a simple piece of code that creates a simulated object based on an interface using the record-and-replay syntax in Rhino Mocks.

Listing 5.3 Creating a dynamic mock object using Rhino Mocks

[Test]

public void Analyze_TooShortFileName_ErrorLoggedToService()

{

MockRepository mocks = new MockRepository(); IWebService simulatedService = mocks.StrictMock<IWebService>();

using(mocks.Record())

{

w Sets simulatedService expectation

.LogError("Filename too short:abc.ext");

}

LogAnalyzer log =

new LogAnalyzer(simulatedService);

 

 

e Invokes

 

 

string tooShortFileName="abc.ext";

 

 

 

 

LogAnalyzer

log.Analyze(tooShortFileName);

 

 

 

 

 

mocks.Verify(simulatedService);

 

 

?

Asserts expectations

 

 

}

 

 

 

have been met

 

 

 

 

 

 

A couple of lines rid us of the need to use a handwritten stub or mock, because they generate one dynamically ?. The simulatedService object instance is a dynamically generated object that implements the IWebService interface, but there’s no implementation inside any of the

IWebService methods.

Next, we set the simulated service object into a “record” state. This is a special state where each method call on the simulated object is recorded as an expectation that something should happen to our simulated object w. These expectations will later be verified by calling mockEngine.Verify-

Dynamically creating a fake object

105

All() or preferably by calling mockEngine.Verify(mockObject). In this

case, we only have one expectation—that LogError() will be called with

the exact input of "file name was too short".

Expectations on mocks and stubs

An expectation on a fake object is an ad hoc rule that’s set on the object.

Expectations on mocks—The rule will usually tell the object that a specific method call on that object is expected to happen later. It may also define how many times it should be called, whether or not the object should throw an exception when that call arrives, or perhaps that the call to this method should never be expected. Expectations are usually set on mock objects during the recording phase of the object, and they’re verified at the end of the test where the mock object lives.

You set expectations on mocks using the static method of MockRepository.StrictMock<T> or MockRespository.DynamicMock<T>.

Expectations on stubs—This wording may feel unintuitive at first, but the rule can tell the stub what value to return based on expected method calls, whether to throw exceptions, and so on. These expectations aren’t to be verified at the end of the test. They’re there so that you can run the test and simulate some alternative reality for your code under test.

You create stubs and set expectations on them using

MockRepository.GenerateStub<T> or the instance method of

MockRepository.Stub<T>.

Then we invoke the object under test—our LogAnalyzer—by injecting it with our mock object and sending in a short filename that should make it invoke the logger internally e.

The last step in this test is to do some sort of assert. In this case, we’ll need to find a way to assert that all the expectations have been met by our mock object (that LogError was indeed called with the correct

string). We can do this by using mocks.Verify(simulatedService) ?.

The Verify(mock) method will go through each of the expectations that we’ve set on our mock object and make sure they’re true (called correctly).

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]