Coding and Dismantling Stuff

Don't thank me, it's what I do.

About the author

Russell is a .Net developer based in Lancashire in the UK.  His day job is as a C# developer for the UK's largest online white-goods retailer, DRL Limited.

His weekend job entails alternately demolishing and constructing various bits of his home, much to the distress of his fiance Kelly, 3-year-old daughter Amelie, and menagerie of pets.

TextBox

  1. Fix dodgy keywords Google is scraping from my blog
  2. Complete migration of NHaml from Google Code to GitHub
  3. ReTelnet Mock Telnet Server à la Jetty
  4. Learn to use Git
  5. Complete beta release FHEMDotNet
  6. Publish FHEMDotNet on Google Code
  7. Learn NancyFX library
  8. Pull RussPAll/NHaml into NHaml/NHaml
  9. Open Source Blackberry Twitter app
  10. Other stuff

Unit Testing Good Patterns #3 - Know Your Moq Argument Matchers

Having spent some time working with Moq, I've found that understanding the argument matchers bundled up in the "It" class is pretty useful stuff. The "It" class is a static class with a few methods on it that are used to tell Moq about the arguments it should expect to receive from your code.

This post is going to be a fairly in depth run through of the core two It methods - It.IsAny<T> and It.Is<T>(a => a....) - followed by a quick skim through the less used It.IsInRange and It.IsRegex methods.

Other posts in this series:

What Are Argument Matchers?

When you're working with Moq, generally you:

  1. Create a Moq object
  2. Use Moq.Setup(x => x.MethodName()) to configure expectations on your Moq object, and what the Moq object should do in response, for example returning a value, throwing an exception, etc.
  3. Pass your Moq object into your code-under-test
  4. Your code-under-test calls methods on the Moq object
  5. The Moq object records every method call it receives.
  6. Moq examines each of these method calls, compares them to any methods that you've configured, and if there's a match, Moq will either behave as configured in step 2 above or return null.
  7. When your code-under-test is finished, you can use Moq.Verify(x => x.MethodName()) to check that the Moq object log contains calls to methods that you expect.

In steps 2 and 7 above, you know what methods you want to setup or verify, but sometimes you don't know or don't care about their arguments, this is where you'll need argument matchers. Remember that in step 6 above Moq is waiting for method calls that it can do something with, when you're doing your Setup in step 2 you can be more or less specific about the arguments that Moq is waiting for.

In the simplest case, you will be mocking methods with no arguments. For example if your code under test does the following:

public static CupOfTea Provide(ITeabag teabag)
{
  var cupOfTea = new CupOfTea();
  teabag.CupOfTea = cupOfTea;
  teabag.Brew();
  return cupOfTea;
}

You could write a simple test to ensure that your teabag is appropriately brewed as follows:

public void Provide_TeaBagIsBrewed()
{
  var mockTeabag = new Mock<ITeabag>();
  teaProvider.Provide(mockTeabag.Object);
  mockTeabag.Verify(x => x.Brew());
}

Note that on the last line, we're verifying that our code called the method "Brew" on our mock teabag. When you write in that lambda expression, Moq doesn't actually execute it, instead it dismantles it and compares it to any invocations it saw to make sure there's a match. But what about method calls that require arguments?

I want my argument to be a specific value

Consider that you want to test the following code:

public static CupOfTea ProvideStrongTea(ITeabag teabag)
{
  var cupOfTea = new CupOfTea();
  teabag.CupOfTea = cupOfTea;
  teabag.Brew(30); // Brew tea for 30 seconds
  return cupOfTea;
}

Note that this time we're asking for particularly strong tea, so we want to verify that when we call the Brew method, we're calling it with an appropriate duration:

public void ProvideStrongTea_TeabagIsBrewedFor30Seconds()
{
  var mockTeabag = new Mock<ITeabag>();
  teaProvider.Provide(mockTeabag.Object);
  mockTeabag.Verify(x => x.Brew(30));
}

Simple enough, right?

Matching Mock Arguments vs Stub Arguments 

Now's probably a good time to talk about Stubs vs Mocks. I like to keep things simple, and use between two different types of fake object (there's plenty of discussion about these two terms, but the following's not a million miles away from consensus):

  • Mock Objects - These are used verify the interactions between your code and the object being mocked. You will normally assert against a Mock, via the Verify method as shown above.
  • Stub Objects - These are used simply to help your code execute, so that you can test other things in your code.

When you're working with Moq, it pays to be very specific about the way you verify against your Mock objects, I think you should know exactly how your code is interacting with the external dependency that you're mocking. But with stubs, the opposite seems to be true. You only use stubs to help your tests complete, so if you're very specific about how you expect stubs to behave, I think you're making your tests too brittle. Every time you change the argument being passed into your stubs, you don't want to have to fix loads of tests.

I don't care what the arguments are.

When you're using a stub, where you shouldn't really care about the arguments, you just need to return something so your test works. In the following code, we've done away with the bog standard old ITeabag - we now have an ITeabagFactory, so we can have a cup of Decaf if we like.

public static CupOfTea ProvideDecafTea(ITeabagFactory teabagFactory)
{
  var cupOfTea = new CupOfTea();
  ITeabag teabag = teabagFactory.Create(TeabagType.Decaf);
  return teabag.Brew(cupOfTea);
}

And now I want to do the simplest test in the world - I want to test that the above code works, and returns us a valid CupOfTea object, as follows:

public void ProvideStrongTea_TeabagIsBrewedFor30Seconds()
{
  var mockTeabagFactory = new Mock<ITeabagFactory>();
  var cupOfTea = teaProvider.Provide(mockTeabagFactory.Object);
  Assert.IsNotNull(cupOfTea);
}

But darn it, we get a NullReferenceException. Inside our code under test, when we call the ITeabagFactory.Create method, we haven't done a Setup on the Moq object, so we get a null teabag back, and noone wants to brew up with a null teabag. Let's fix our test:

public void ProvideStrongTea_TeabagIsBrewedFor30Seconds()
{
  var mockTeabagFactory = new Mock<ITeabagFactory>();
  mockTeabagFactory.Setup(x => x.Create(It.IsAny<TeabagType>())).Returns(new DecafTeabag());
  var cupOfTea = teaProvider.Provide(mockTeabagFactory.Object);
  Assert.IsNotNull(cupOfTea);
}

We've added a Setup that returns a new DecafTeabag, but note that I'm now using "It.IsAny<TeabagType>()" in my setup instead of the literal "TeabagType.Decaf". Remeber, Moq doesn't actually execute the Create method, it's breaking it up into bits, and saying, "If I get any calls to the Create method, and I get an argument that's a valid TeabagType, then I'm going to return a new DecafTeabag object."

I just want to make sure the argument's not null

I had a chat with a co-ninja at work the other day, where we made the suprising discovery that as far as Moq is concerned, if you pass in a null object, it still considers that to be a match according to the It.IsAny<T>() matcher. After much rumination, we decided we were cool with that, but it did leave the question, how do we make sure that at least an object is not null? And on that note, why would you want to do this anyhow?

public static CupOfTea ProvideTea(ITeabag teabag)
{
  var cupOfTea = null;
  teabag.Brew(cupOfTea);
  return cupOfTea;
}

The above code snippet is a variant on the first example, but with an obvious bug added - we're attempting to brew our teabag in a null cup! (I'm struggling to extract any drama out of this fictional scenario) Let's write a simple test to catch this:

public void ProvideTea_CupOfTeaIsNotNull()
{
  var mockTeabag = new Mock<ITeabag>();
  teaProvider.ProvideTea(mockTeabag.Object);
  mockTeabag.Verify(x => x.Brew(It.Is<CupOfTea>(a => a != null)));
}

We're now using a new matcher, the "It.Is<T>(a => a...)" matcher. Moq compares the actual invocations to the setups/verifies you've configured, it looks at the argument it's received from your code (in this case null), and it runs the argument through the lambda specified (in this case a => a != null). In our case this lambda returns "false", so Moq fails the verify statement and fails the test.

Hint : Be careful not to reuse the "x" token in your argument matcher lambda expression by the way, I recall that that caused us a little head scratching!

Now onto our second question - why would you want to assert that your argument's not null? The only valid case I can think is the one above, where we're constructing an object inside our code-under-test and using it immediately, so there's no way we can know anything about this object other than it exists. In general however, I think checking that argument are not null is a fail. Remember my suggestion above re mocks vs stubs, you either want to have strong asserts or no asserts, this is an uncomfortable middle ground.

I want my argument to have specific criteria

Okay, enough of the dumb ITeabag stuff, if you've stuck with it this far you obviously get the gist, so let's quickly nip through the next few matchers available to us.

We've seen the "It.IsAny<T>(a => a...)" argument matcher above, you can actually do anything you like with your argument using this matcher. You could for example have a method that validates the argument against all sorts of criteria and returns a true or false, you could then have a verify that was something like:

mockTeabag.Verify(x => x.Brew(It.Is<CupOfTea>(a => ValidCupForTea(a)));

...

bool ValidCupForTea(CupOfTea cupOfTea)
{
  return (cupOfTea.IsClean)
    && (cupOfTea.IsHeatProof)
    && (cupOfTea.HasHandle);
}

My argument must be in a valid range

I've had very little cause to use this matcher myself yet, but it's fairly self-explanatory, its usage is something like:

mockKettle.Verify(x => x.HeatWaterToTemperature(It.IsInRange<int>(85, 100, Range.Inclusive)));

Note that you can setup here whether the range is inclusive or exclusive,

My argument must match a regex pattern

I can imagine more uses for this matcher, but again I've not had much need to use it myself. This one goes something like:

mockTeabagFactory.Verify(x => x.GetTeabagByName(It.IsRegex("Premium")));

The options are the standard Microsoft RegexOptions enum.

And that concludes my quick runthrough of the Moq argument matchers. Hope you enjoyed!


Permalink | Comments (0)

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading