Monday, October 05, 2009

TotT: Making a Perfect Matcher

by Zhanyong G. Mock Wan in Google Kirkland

In the previous episode, we showed how Google C++ Mocking Framework matchers can make both your test code and your test output readable. What if you cannot find the right matcher for the task?

Don't settle for anything less than perfect. It's easy to create a matcher that does exactly what you want, either by composing from existing matchers or by writing one from scratch.

The simplest composite matcher is Not(m), which negates matcher m as you may have guessed. We also have AnyOf(m1, ..., mn) for OR-ing and AllOf(m1, ..., mn) for AND-ing. Combining them wisely and you can get a lot done. For example,

EXPECT_THAT(new_code, AnyOf(StartsWith(“// Tests”)),
              Not(ContainsRegex(“TODO.*intern”))));

could generate a message like:

Expected: (starts with “// Tests”) or
          (doesn't contain regular expression “TODO.*intern”)
Actual: “/* TODO: hire an intern. */ int main() {}”

If the matcher expression gets too complex, or your matcher logic cannot be expressed in terms of existing matchers, you can use plain C++. The MATCHER macro lets you define a named matcher:

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

allows you to write EXPECT_THAT(paren_num, IsEven()) to verify that paren_num is divisible by two. The special variable arg refers to the value being validated (paren_num in this case) – it is not a global variable.

You can put any code between {} to validate arg, as long as it returns a bool value.

The empty string “” tells Google C++ Mocking Framework to automatically generate the matcher's description from its name (therefore you'll see “Expected: is even” when the match fails). As long as you pick a descriptive name, you get a good description for free.

You can also give multiple parameters to a matcher, or customize its description. The code:

// P2 means the matcher has 2 parameters. Their names are low and high.

MATCHER_P2(InClosedRange, low, high, “is in range [%(low)s, %(high)s]”) {
  return low <= arg && arg <= high;
}
...
EXPECT_THAT(my_age, InClosedRange(adult_min, penalty_to_withdraw_401k));

may print:

Expected: is in range [18, 60]
  Actual: 2

(No, that's not my real age.) Note how you can use Python-style interpolation in the description string to print the matcher parameters.
You may wonder why we haven't seen any types in the examples. Rest assured that all the code we showed you is type-safe. Google C++ Mocking Framework uses compiler type inference to “write” the matcher parameter types for you, so that you can spend the time on actually writing tests – or finding your perfect match.

Toilet-Friendly Version

2 comments:

  1. I just do not get some of these matchers. Unit tests are meant to be consistent and repeatable, so I would be interested to find why you think there is a need for such matchers as the "in range" and "is even" ?
    Surely the tests should be:
    given a number x it is guaranteed to give number y. If you can not guarantee the output then it seems the code being tested has a smell, this being that it depends on global state.

    ReplyDelete
  2. Hi Zhanyong,

    My name is Gil Zilberfeld, and with my colleague Roy Osherove, we do a little cast called This week in testing.

    This week we mention the new matcher feature, and talk about the Google mocking framework. I invite you to watch. If you like what you see, please talk about us, so more people can enjoy.

    Thanks,

    Gil Zilberfeld
    Typemock

    ReplyDelete

The comments you read and contribute here belong only to the person who posted them. We reserve the right to remove off-topic comments.