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

110

CHAPTER 5

Isolation (mock object) frameworks

You can also use LastCall to set the method call to throw a specific exception:

LastCall.Throw(Exception)

Or you can even execute your own delegate:

LastCall.Do(yourdelegatehere)

Again, it’s usually a bad idea to tell a mock to return a value and also to verify mock expectations. In this case, your test may be overspecified; it checks too many things and can break easily. A fake object should either be used as a mock or as a stub, not both.

Stubs are usually more appropriate for returning fake values than mock objects, and isolation frameworks can also create them.

5.5 Creating smart stubs with an isolation framework

A stub returns the appropriate responses when called and is never used to see if a test passes or not. Calling VerifyAll() or Verify(stub) won’t verify anything against stub objects—only against mocks. Most isolation frameworks contain the semantic notion of a stub, and Rhino Mocks is no exception.

5.5.1Creating a stub in Rhino Mocks

Listing 5.6 shows how you create a stub object in Rhino Mocks.

Listing 5.6 Creating a stub is remarkably similar to creating a mock object

[Test]

public void ReturnResultsFromStub()

{

MockRepository mocks = new MockRepository(); IGetResults resultGetter = mocks.Stub<IGetResults>(); using(mocks.Record())

{

resultGetter.GetSomeNumber("a");

LastCall.Return(1);

}

Creating smart stubs with an isolation framework

111

int result = resultGetter.GetSomeNumber("a"); Assert.AreEqual(1, result);

}

The syntax for creating a stub is almost the same as for mock objects. But consider what happens if you run a test that doesn’t call an expected method on the stub, but still verifies expectations. This is shown in listing 5.7.

Listing 5.7 Verifying expectations on a stub object can’t fail a test

[Test]

public void StubNeverFailsTheTest()

{

MockRepository mocks = new MockRepository(); IGetResults resultGetter = mocks.Stub<IGetResults>(); using(mocks.Record())

{

resultGetter.GetSomeNumber("a");

 

 

Specifies a rule,

 

LastCall.Return(1);

 

 

not an expectation

}

 

 

 

 

resultGetter.GetSomeNumber("b");

 

 

 

mocks.Verify(resultGetter);

 

 

 

Will never fail on stubs

 

 

 

}

The test in listing 5.7 will still pass because the stub, by definition, is incapable of breaking a test. Any expectations set on it are purely to determine the return value or the exceptions they should throw.

Rhino Mocks contains a handy feature that isn’t supported by most frameworks (except Typemock Isolator). For simple properties on stub objects, get and set properties are automatically implemented and can be used as if the stub were a real object. You can still set up a return value for a property, but you don’t need to do it for each and every property on a stub object. Here’s an example:

ISomeInterfaceWithProperties stub = mocks.Stub<ISomeInterfaceWithProperties>();

112

CHAPTER 5

Isolation (mock object) frameworks

stub.Name = "Itamar"; Assert.AreEqual("Itamar",stub.Name);

We can also simulate an exception using expectations on a stub. Listing 5.8 shows how you would simulate an OutOfMemoryException.

Listing 5.8 Faking an exception using the LastCall class

[Test]

public void StubSimulatingException()

{

MockRepository mocks = new MockRepository(); IGetResults resultGetter = mocks.Stub<IGetResults>(); using(mocks.Record())

{

resultGetter.GetSomeNumber("A");

LastCall.Throw(

new OutOfMemoryException("The system is out of memory!") );

}

resultGetter.GetSomeNumber("A");

}

This test will fail due to a nasty out-of-memory exception. That’s how easy it is.

In the next section, we’ll use this ability to simulate errors when testing a more complex scenario.

5.5.2Combining dynamic stubs and mocks

We’ll use the example from listing 4.2 in chapter 4, where we talked

about LogAnalyzer using a MailSender class and a WebService class. This

is shown in figure 5.1.

We want to make sure that, if the service throws an exception, LogAnalyzer will use MailSender to send an email to an administrator.

Creating smart stubs with an isolation framework

113

Figure 5.1 The web service will be stubbed out to simulate an exception, and the email sender will be mocked to see if it was called correctly. The whole test will be about how LogAnalyzer interacts with other objects.

Listing 5.9 shows what the logic looks like with all the tests passing.

Listing 5.9 The method under test and a test that uses handwritten mocks and stubs

public void Analyze(string fileName)

{

if(fileName.Length<8)

{

try

{

service.LogError("Filename too short:" + fileName);

}

catch (Exception e)

{

email.SendEmail("a","subject",e.Message);

}

}

//..other logic

}

114

CHAPTER 5

Isolation (mock object) frameworks

[Test]

public void Analyze_WebServiceThrows_SendsEmail()

{

StubService stubService = new StubService(); stubService.ToThrow= new Exception("fake exception");

MockEmailService mockEmail = new MockEmailService();

LogAnalyzer2 log = new LogAnalyzer2(); //we use setters instead of

//constructor parameters for easier coding log.Service = stubService; log.Email=mockEmail;

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

Assert.AreEqual("a",mockEmail.To); Assert.AreEqual("fake exception",mockEmail.Body); Assert.AreEqual("subject",mockEmail.Subject);

}

}

public class StubService:IWebService

{

...

}

public class MockEmailService:IEmailService

{

...

}

Listing 5.10 shows what the test could look like if we used Rhino Mocks.

Listing 5.10 Converting the previous test into one that uses dynamic mocks and stubs

[Test]

public void Analyze_WebServiceThrows_SendsEmail()

{

MockRepository mocks = new MockRepository(); IWebService stubService =

mocks.Stub<IWebService>(); IEmailService mockEmail =

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