Supercharge Isolate.Verify
What is this verify you talk about?
Any Isolation framework worth mentioning can do the following three features:
- Create fake object (a.k.a mock/stub/test dummy – you name it)
- Set expectations on fake objects
- Verify method was called in fake object
Verify is what differentiate mock from stub. Without getting into too many definitions a mock object is a fake object that can assert that methods were called inside of it and even check if it was called with specific arguments.
So what’s the problem with verify?
Verify is a powerful tool and should be used with care. Checking that method A called method B is not its purpose. You don’t want your tests failing just because you did a simple refactoring. Instead Verify should be used when the end result of the test is a method call and not a state, for example use it in a test that an email is sent to the client after a successful operation.
Oddly enough – that’s not the reason that developers tend not to like using it. The real reason is that unlike Assert that provides a clear and understandable message when fails some of the times Verify message is not clear enough. Good errors are crucial when test fails – it saves time by helping the developer understand the issue without needing to debug the code which equals faster development time.
And so we would like to improve the error message on the failing test to help understand why the test failed a few days/weeks/months after it was written.
Improving Verify error messages
Luckily for us the good people of Typemock have already thought about this issue as long as enough information is provided.
[Test]
public void VerifyStringParam()
{
var cut = Isolate.Fake.Instance<ClassUnderTest>();
cut.SomeMethod("this is the wrong string");
Isolate.Verify.WasCalledWithExactArguments(() => cut.SomeMethod("this is the right string"));
}
The code is quite simple even if you’re not familiar with Isolator – in fact it’s too simple, I cannot think of a good reason to write this code other than to show my point.
First we create a fake object then we call a method on it and finally we check that it was called with a different variable.
We got the following error:
The error is exactly what I need – it tells me what went wrong and shows the first offending character in the sting. But what happens if I use a more complex parameter:
[Test]
public void VerifyComplexParam()
{
var cut = Isolate.Fake.Instance<ClassUnderTest>();
var myClass = new MyClass
{
Id = "wrongId",
Data = 11
};
cut.SomeMethod(myClass);
var expected = new MyClass { Id = "Id", Data = 5 };
Isolate.Verify.WasCalledWithExactArguments(() => cut.SomeMethod(expected));
}
Here I use a more elaborate (yet useless) class as a parameter, and again I “accidently” call the method with the wrong parameter. Running the test would provide the following failure message:
SO what happened? the test failed but Isolator in his great wisdom offers us to implement Equals because 9 out of 10 times I don’t want to compare the parameters by reference just by their inside value. By implementing equality methods we can tell Isolator if the inside of the class is the same – after implementing Equals I get the following error message:
How cool is that! Isolator has used equals and when it failed assumed that one (or more) of the properties of the class are to blame and so it shows all of the properties in MyClass that are not equal.
But what happens if we pass a really complex class – one that contains another class?
[Test]
public void VerifyMoreComplexParam()
{
var cut = Isolate.Fake.Instance<ClassUnderTest>();
var complexClass = new ComplexClass
{
InnerClass = new MyClass { Id = "id", Data = 10 }
};
cut.SomeMethod(complexClass);
var expected = new ComplexClass
{
InnerClass = new MyClass { Id = "Id", Data = 5 }
};
Isolate.Verify.WasCalledWithExactArguments(() => cut.SomeMethod(expected));
}
We get this error message:
Isolator has found that the problem was the parameter (duh) but he would not tell me what is wrong with it – I guess there is no option but to debug – wrong!
By overriding ToString we can improve the error message into this:
Even if Isolator won’t guess what is different between the classes at least he shows the string representation helping me discover for myself why the test failed.
You might have noticed that when using these simple tricks you can discover instantly why the test failed – without using debugger or even glancing at the source code – in fact throughout this post I never once showed the code for the class under test.
Happy coding…
Reference: Supercharge Isolate.Verify from our NCG partner Dror Helper at the Helper Code blog.