Friday, April 17, 2009

Create Your Own Matchers with Ease

Earlier I showed how you can use Google Mock matchers to make both your test code and your test output readable.  But what if the matcher you need isn't provided by Google Mock?

Don't fret -- you can roll your own matchers easily, either by building on existing ones or writing them from scratch. For the former, Google Mock gives you several composite matchers.  For the latter, you can use some simple macros.

Writing a Composite Matcher

The simplest composite matcher is Not(matcher), which negates the given matcher, as you probably have guessed.

Here's an example: ContainsRegex(s) is a matcher verifying that a string contains regular expression s as a substring.  To verify that a string does not contain the regular expression, write:

EXPECT_THAT(GetUserResponse(), Not(ContainsRegex("Acho+!")));

which may produce a message like:

Value of: GetUserResponse()
Expected: doesn't contain regular expression "Acho+!"
  Actual: "Ah! Achooo!"

AnyOf(matcher_1, ..., matcher_k) is another composite matcher you can use.  It asserts that at least one of the k matchers matches the value.  Therefore

EXPECT_THAT(s, AnyOf(StartsWith("Wow"), ContainsRegex("Acho+!")));

may print:

Value of: s
Expected: (starts with "Wow") or (contains regular expression "Acho+!")
  Actual: "Aha!"

Note how the matcher descriptions are combined.  There is also AllOf(matcher_1, ..., matcher_k) for asserting that all k matchers match the value.

Writing a Matcher from Scratch

Defining matchers from scratch is easy too.  Just use the MATCHER() or MATCHER_Pn() macro.  Here are some examples:

To define a simple matcher to verify that a number is even, just write:

MATCHER(IsEven, "") { return (arg % 2) == 0; }

Ignore the "" in the argument list for now.  As you probably have guessed, the matcher tests whether the argument (referred to by arg) is divisible by 2 -- you can put any code you want inside the {} to verify the argument.  With that, you can write:

EXPECT_THAT(Foo(5), IsEven());

and it may print:

Value of: Foo(5)
Expected: is even
  Actual: 9

The matcher description "is even" is automatically calculated from the matcher's name.  As long as you pick a good name for your matcher, you get the readable description for free.

A matcher can have parameters (as in the example of ContainsRegex("...")).  Such parameterized matchers can be defined using MATCHER_P or MATCHER_Pn, where n is the number of parameters:

MATCHER_P(Contains, element, "") {
  return std::find(arg.begin(), arg.end(), element) != arg.end();
EXPECT_THAT(prime_set, Not(Contains(12)));
EXPECT_THAT(user_list, Contains(my_user_id));

may print:

Value of: prime_set
Expected: not (contains 12)
  Actual: { 2, 3, 5, 7, 12 }

Value of: user_list
Expected: contains "wan"
  Actual: { "joe", "jon", "nat" }

Note that Contains() is polymorphic: it was first used to match a set<int>, and then used to match a list<string>.  The description 'contains "wan"' is automatically generated from the matcher name and its parameter -- you don't need to worry about a thing.

Also note that you don't need to write the type of the value being matched and the parameter types: the macro automatically infers all of them for you too.

What if you aren't happy with the auto-generated description?  You can override it.  Just write a description pattern instead of "" inside the MATCHER*() macro:

MATCHER_P2(InClosedRange, low, high, "is in range [%(low)s, %(high)s]") {
  return low <= arg && arg <= high;
EXPECT_THAT(my_age, InClosedRange(adult_min, can_withdraw_401k));

may print:

Value of: my_age
Expected: is in range [18, 60]
  Actual: 71

Note how you can refer to the matcher parameters in the description pattern.

Interested?  You can learn more about the usage of MATCHER* here.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.