This documentation applies to NMock 1.x and is no longer being maintained.
NMock is a dynamic mock-object library for .NET. Its purpose is to provide a clean API for rapidly creating mock implementations of custom objects and verifying that objects interact with them correctly from unit tests.
A mock:
- takes on the interface of another object, allowing it to be substituted for a real one for testing purposes.
- allows Expectations to be setup, specifying how the class under test is expected to interact with the mock.
- fails the test if any of the expectations are violated.
- can also act as a stub, allowing the test to specify objects to be returned from mocked methods.
Features specific to NMock:
- expectations are specified before hand and verified on the fly as the code under test is being executed, rather than afterwards using assertions. This has the advantage that mocks fail fast, allowing you to easily pinpoint the exact point the test failed, using a stack trace or debugger.
- mock implementations of interfaces (and classes with virtual methods) are generated on the fly at runtime. Avoids having to add a codegen step to the build.
- error messages clearly show the reason for the failure.
- flexible expectations can be built up using the Constraint library.
Basics
A simple example which demonstrates some of the features of NMock:
class ClassUnderTest { // the class under test. public Something something; // an external object. public ClassUnderTest( Something something ) { this.something = something; } // method under test. public void DoYourStuff() { if ( someComplicatedLogic() ) { something.Eat( "cheese", 22, true ); // interaction with external object. } else { something.Nap(); // another interaction } } } // class that's to be mocked. Note that methods are virtual. // the implementations of these methods are not called during the test as they are mocked out. class Something { public virtual void Eat( string food, int numChews, bool eatWithMouthClosed ) { ... } public virtual void Nap() { ... } } [ Test ] public void SomethingTest() { // SETUP Mock mock = new DynamicMock(typeof(Something)); // create a mock ClassUnderTest classUnderTest = new ClassUnderTest((Something)mock.MockInstance); // pass mock into ClassUnderTest. // note that you get the mocked instance by calling Mock.MockInstance and casting. // EXPECTATIONS : how we expect the ClassUnderTest to deal with mock. mock.Expect("Eat", new IsEqual("cheese"), new IsAnything(), new IsAnything()); // see below mock.ExpectNotCalled("Nap"); // EXECUTE : any unexpected calls on the mock will fail here. classUnderTest.DoYourStuff(); // VERIFY: checks that all expectations have been met mock.Verify(); // note: no Assertions. }
The important line is:
mock.Expect("Eat", new IsEqual("cheese"), new IsAnything(), new IsAnything());
This says: expect the Eat() method to be called on the mock, and the three arguments must match the following constraints:
arg1 == "cheese", arg2 is anything, arg3 is anything.
If Eat() is called with any arguments that don't match these constraints, the test will fail fast.
Constraints
For an expectation to be fulfilled, each parameter passed in by the code under test must match a Constraint.
Constraints are mini rules, such as IsEqual(), IsNotEqual(), IsNull(), IsAnything(), etc. NMock comes with a library of flexible constraints. The main constraints (with examples) are:
|
|
Example |
Values that will pass |
Values that will fail |
|
|
|
|
|
Basic constraints |
Most common constraints for testing a value |
|
|
|
IsEqual(object expected) |
Passes if expected.Equals(actual) |
new IsEqual("hello"); |
"hello" |
"boo", "HELLO", " hello " |
IsNull() |
Passes if actual is null |
new IsNull(); |
Null |
"thing" |
IsAnything() |
Always passes |
new IsAnything(); |
"foo", 3, new Thingy(), null |
(nothing!) |
IsTypeOf(Type type); |
Passes if actual is of a specific type |
new IsTypeOf(typeof(IList)); |
new ArrayList(); |
new HashTable(); |
IsIn(param object[] list) |
Passes if actual is equal to any specified value |
new IsIn("a", "b", "c"); |
"a", "b", "c" |
"d" |
|
|
|
|
|
Chained constraints |
Allow contraints to be nested for logic |
|
|
|
Not(IConstraint constraint) |
Passes only if the constraint fails |
new Not(new IsNull()); |
"hello" |
null |
Or(IConstraint a, IConstraint b) |
Passes if either constraint passes |
new Or(new IsEqual("cheese"), new IsEqual("food")) |
"cheese", "food" |
"parrot" |
And(IConstraint a, IConstraint b) |
Passes if both constraints pass |
|
|
|
NotEqual() |
Same as new Not(new IsEqual(…)) |
|
|
|
NotNull() |
Same as new Not(new IsNull()) |
|
|
|
NotIn(param object[] list) |
Same as new Not(new IsIn(…)) |
|
|
|
|
|
|
|
|
String Constraints |
Operate on the ToString() representation of the objects |
|
|
|
IsEqualIgnoreCase(object o) |
Tests strings, ignoring case |
new IsEqualIgnoreCase("hello") |
"hello", "HELLO", "hEllO" |
"world", 3, " hello " |
IsEqualIgnoreWhiteSpace(object o) |
Tests strings, ignoring whitespace |
new IsEqualIgnoreWhiteSpace("hello") |
"hello", " hello ", "h e l\n\rlo" |
"HELLO", "foo" |
IsMatch(Regex regex) |
Tests if actual matches a regular expression |
new IsMatch(new Regex("f.*t")) |
"fat", "fit", "fart", "foo pat" |
"farm" |
StartsWith(string prefix) |
Tests if actual starts a value |
new StartsWith("wa"); |
"wart", "wa" |
"kawa" |
|
|
|
|
|
Misc Constraints |
|
|
|
|
IsCloseTo(double expected, double tolerance) |
Passes if actual is within tolerance of expected |
new IsCloseTo(500.0, 0.5) |
500.0, 499.6, 500.4 |
499.0, 501.1 |
IsListEqual(IList expected) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Special Property Constraint |
|
|
|
|
PropertyIs(string propertyName, object expected) |
Tests the value of the property of a value instead |
|
|
|
|
|
|
|
|
|
Example |
|
|
|
|
new PropertyIs("Customer.Address.Street", new IsEqual("Elm Street")) |
|
|
|
|
This will pass if actual.Customer.Address.Street == "Elm Street" |
|
|
|
The power of constraints is that you can flexibly specify your expectations before your code under test runs, allowing fail fast.
You can easily create custom constraints by implementing the IConstraint interface, or passing a delegate into the Constraint class.
Tips
Syntactic sugar: Because 90% of the time, the two constraints you use are IsEqual() and IsAnything(), these can be replaced with object and null arguments respectively. The above example can be shortened to mock.Expect("Eat", "cheese", null, null).
Test layout: Tests that use mocks are easier to read if they follow a standard layout. We break the test into four parts: "Setup" initializes any objects/mocks and puts them into the correct state before a test. "Expectations" specifies what you expect to happen - this is the actual 'test' bit. "Execute" runs the code under test - this is where you'd typically get a failure. "Verify" performs any post-execution checks including verifying all the mocks expectations have been met and any additional assertions.
Externalize dependencies: Inversion of Control (AKA Dependency Inversion Principle, Dependency Injector Pattern). Passing dependencies in from the outside decouples class under test from a particular implementation of the dependency (a good practice in general), making it easier to mock.
Return values: If the method being mocked returns a value or throws an exception, you can use ExpectAndReturn() or ExpectAndThrow() to specify what the mock should return. You can also use SetupResult() which does not associate an expectation and doesn't care how many times the method is called.
Mocking classes: NMock supports mocking of classes with virtual methods as well as interfaces. The merits of when to mock classes and interfaces is a religious debate. However, use of interfaces is highly recommended.
Don't mock things you don't own: It's awkward and painful trying to mock someone else's API (such as ADO.NET), leading to hard to read tests and assumptions about how the API works. On the other hand, mocking your own APIs leads you down the path of creating cleaner interfaces, which can never hurt. If dealing with external APIs, create your own clean abstraction suited for your purposes, which you can mock easily. For testing the abstraction itself, don't use mocks, use the real thing. This is the opposite of the usage commonly associated with using mocks, which usually leads to brittle and cumbersome tests.
PropertyConstraint: Allows you to setup constraints on properties of objects.