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

Parameter constraints for mocks and stubs

115

mocks.StrictMock<IEmailService>();

 

 

using(mocks.Record())

 

 

{

 

 

Ensures

 

stubService.LogError("whatever");

 

? exception

LastCall.Constraints(Is.Anything());

 

 

is thrown

 

 

 

LastCall.Throw(new Exception("fake exception"));

mockEmail.SendEmail("a","subject","fake exception");

}

LogAnalyzer2 log = new LogAnalyzer2(); log.Service = stubService;

log.Email = mockEmail;

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

mocks.VerifyAll();

}

The nice thing about this test is that it requires no handwritten mocks, and it’s still readable for the developer.

You might notice a line in listing 5.10 that you haven’t come across yet ?. That’s a parameter constraint that makes sure the exception is thrown no matter what we send the stub as a parameter. Parameter constraints are the topic of the next section.

5.6 Parameter constraints for mocks and stubs

Isolation frameworks enable us to easily test the value of parameters being passed into our mock objects. In the coming sections, we’ll look at the various ways you can check parameters, such as strings, properties, and full object models, using very little code.

5.6.1Checking parameters with string constraints

Consider the following scenario. We’d like to test that our LogAnalyzer sends a message of this nature to the error log:

"[Some GUID] Error message"

116

CHAPTER 5

Isolation (mock object) frameworks

Here’s an example:

"33DFCC9D-D6C5-45ea-A520-A6018C88E490 Out of memory"

In our test, we don’t care about the GUID (globally unique identifier) at the beginning, but we care what the error message is. In fact, we don’t really have control over the GUID. (We could gain control by creating some sort of IGuidCreator interface and stubbing it out in the test, but that might prove a little too much work for what we need.)

Parameter constraints allow us to specify demands or rules to our mocks and stubs for each specific parameter to a method call. Isolation frameworks allow you to create these parameter constraints, and each framework has its own syntax for doing so.

The simplest way to use constraints, as shown in listing 5.11, is by using the LastCall class in conjunction with one of the constraints classes. In our case, it would be the Contains class, which takes as a constructor the inner string to search for.

Listing 5.11 Using a string constraint in a test

[Test]

public void SimpleStringConstraints()

{

MockRepository mocks = new MockRepository();

IWebService mockService = mocks.CreateMock<IWebService>(); using (mocks.Record())

{

mockService.LogError("ignored string"); LastCall.Constraints(new Contains("abc"));

}

mockService.LogError(Guid.NewGuid() + " abc"); mocks.VerifyAll();

}

Using the LastCall.Constraints() method, we can send in a constraint object (which has to inherit from AbstractConstraint, defined as part of Rhino Mocks) for each parameter the method expects. There are four major “helper” classes for constraints in Rhino Mocks, listed in

Parameter constraints for mocks and stubs

117

table 5.1. For string-related constraints, we have Contains, EndsWith, Like, and StartsWith. All of these classes take a string at the constructor. To help you use these constraints, Rhino Mocks includes a helper class called Text that has static methods to return these constraint objects.

Here’s the same test using the Text class:

LastCall.Constraints(Text.Contains("abc"));

Table 5.1 The four types of constraints in Rhino Mocks

Helper class

Description

Methods

 

 

 

Text

Checks string-related constraints

Contains(string)

 

 

EndsWith(string)

 

 

StartsWith(string)

 

 

Like(string)

 

 

 

List

Checks collection-related con-

Count(constraint)

 

straints

Element(int, constraint)

 

 

 

 

Equal(IEnumerable)

 

 

IsIn(object)

 

 

OneOf(IEnumerable)

 

 

 

Is

Checks the direct value of param-

Anything()

 

eters passed in

Equal(object)

 

 

 

 

GreaterThan(IComparable)

 

 

LessThan(IComparable)

 

 

Matching<T>(Predicate<T>)

 

 

NotNull()

 

 

Same(object)

 

 

TypeOf(Type)

 

 

 

Property

Checks the value of a specific

IsNull()

 

property on an object that’s

Value(Type, PropertyName, object)

 

passed in as a parameter

 

ValueConstraint(Type,

 

 

 

 

PropertyName, constraint)

 

 

 

And, Or

Allows combining multiple con-

 

 

straints into a single logical con-

 

 

straint

 

 

 

 

Callback

Allows triggering a custom dele-

 

 

gate whenever the method is

 

 

called

 

 

 

 

118

CHAPTER 5

Isolation (mock object) frameworks

Some of the more interesting constraints are Property constraints, And and Or constraints, and Callback constraints. Let’s review them one by one.

5.6.2Checking parameter object properties with constraints

Assume that the IWebService interface has a method that expects to take in a TraceMessage object, which contains specific rules about its properties. We could easily check the values of the passed-in TraceMessage object properties by using the Property-related constraints. This is shown in listing 5.12.

Listing 5.12 Using the Property constraints by using the Property static class

[Test]

public void ConstraintsAgainstObjectPropeties()

{

MockRepository mocks = new MockRepository();

IWebService mockservice = mocks.CreateMock<IWebService>();

using (mocks.Record())

 

 

{

 

 

mockservice.LogError(new TraceMessage("",0,""));

 

 

LastCall.Constraints(

 

 

Property.Value("Message", "expected msg")

 

Checks

 

&& Property.Value("Severity", 100)

 

 

property

&& Property.Value("Source", "Some Source"));

 

values

}

mockservice.LogError(new TraceMessage("",1,"Some Source")); mocks.VerifyAll();

}

Notice the use of the && operators here.

Combining constraints with AND and OR

The && operators in listing 5.12 are overloaded to use a special And constraint, which requires all of these constraints to be true for the test to pass. You could also use the || overload to set a special Or constraint, which only requires one of the constraints to be true.

The And and Or constraints both take two AbstractConstraint objects in their constructor. The previous example in listing 5.12 combines two And constraints and could have been written as in listing 5.13.

Parameter constraints for mocks and stubs

119

Listing 5.13 Combining constraints with And and Or

And combined1 = new And(

Property.Value("Message", "expected msg" ), Property.Value("Severity", 100));

And combined2 =

new And(combined1,

Property.Value("Source", "Some Source"));

LastCall.Constraints(combined2);

As you can see, this sort of syntax can get messy pretty fast, and I don’t have much use for it. This is where using a manual stub or a callback can make things much clearer, instead of using the And/Or syntax.

Comparing full objects against each other

If we’re going to test things in the simplest way, we could compare two objects. We could send the “expected” object with all the expected properties set as part of the recording process (no need for constraints), and then call verify(), as shown in listing 5.14.

Listing 5.14 Comparing full objects

[Test]

public void TestingObjectPropertiesWithObjects()

{

MockRepository mocks = new MockRepository();

IWebService mockservice = mocks.CreateMock<IWebService>(); using (mocks.Record())

{

mockservice.LogError(

new TraceMessage("Some Message",100,"Some Source"));

}

mockservice.LogError(new TraceMessage("",1,"Some Source")); mocks.VerifyAll(); //this should fail the test

}

Testing full objects only works for cases where

it’s easy to create the object with the expected properties.

you want to test all the properties of the object in question.

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