The new feature encapsulated by MatcherAttibute in Moq allows developers to create quite easy argument matchers.
In a scenario with a Customer class and a IFooService:
public class Customer { public string Name { get; set; } public int Age { get; set; } // … } public interface IFooService { void Bar(Customer c); }
If in a test you need an expectation on Bar method that stands for a Customer c where c.Age >= 18 you needed to do something like this:
[Test] public void Test1() { var mock = new Mock<IFooService>(MockBehavior.Strict); mock.Expect(x => x.Bar(It.Is<Customer>(c => c.Age >= 18))); // … }
but from now on, using the MatcherAttribute the same expectation could be written as follows:
[Test] public void Test1() { var mock = new Mock<IFooService>(MockBehavior.Strict); mock.Expect(x => x.Bar(GrownUp())); // … } [Matcher] public static Customer GrownUp() { return null; } public static bool GrownUp(Customer c) { return c.Age >= 18; }
Lets call the first GrownUp method as Matcher declaration, and the second Matcher implementation.
- Matcher declaration must has return type Customer in order to allow the expectation to compile, and be applied the MatcherAttribute.
- Matcher implementation is used in the actual match, receiving the Customer to be matched and returning the result of the match.
- Matcher declaration is never executed by Moq, it is used to have clear syntax.
More arguments
Also you could have more arguments involved in the matching, just add the at the end of both declaration and implementation.
For example to match older than a certain age:
[Test] public void Test1() { var mock = new Mock<IFooService>(MockBehavior.Strict); mock.Expect(x => x.Bar(OlderThan(18))); // … } [Matcher] public static Customer OlderThan(int minimumAge) { return null; } public static bool OlderThan(Customer c, int minimumAge) { return c.Age >= minimumAge; }
Your own helper class
To reuse some matchers, you could define them in a separate a class instead of inside the fixture.
[Test] public void Test1() { var mock = new Mock<IFooService>(MockBehavior.Strict); mock.Expect(x => x.Bar(An.OlderThan(18))); // … } static class An { [Matcher] public static Customer OlderThan(int minimumAge) { return null; } public static bool OlderThan(Customer c, int minimumAge) { return c.Age >= minimumAge; } }
Non static method
Finally, the matchers declaration and implementation don't need to be static. So you could have additional context of matching.
Although I suggest this last only in certain scenarios, for example, match enumeration of values:
[Test] public void Test2() { var mock = new Mock<IFooService>(MockBehavior.Strict); var names = new E(new [] { “John Doe”, “Brian Cardiff” }); mock.Expect(x => x.Bar(names.Some())); // this will success // mock.Object.Bar(new Customer { Name = “Brian Cardiff” }); // this will fail // mock.Object.Bar(new Customer { Name = “Sandy” }); } public class E { IEnumerable<string> names; public E(IEnumerable<string> names) { this.names = names; } [Matcher] public Customer Some() { return null; } public bool Some(Customer s) { return names.Contains(s.Name); } }