Find out where is the method that was called unexpectedly using fake objects

Every Isolation/Mocking framework out there can verify that a method of a fake object was called and not less important make sure that certain methods are not called.

Let’s say that I want to make sure that a method didn’t encounter any errors during execution but verifying that logger.Error was not called:

[TestFixture]
public class MyClassTests
{
[Test]
public void RunMethod_LogErrorNotInvoked()
{
// Arrange
var fakeLogger = Isolate.Fake.Instance();
Isolate.WhenCalled(() => LogManager.GetLogger(typeof(MyClass))).WillReturn(fakeLogger);

// Act
var classUnderTest = new MyClass();
classUnderTest.RunMethod();

// Assert
Isolate.Verify.WasNotCalled(() => fakeLogger.Error(null));
}
}

Note:

Before we continue keep in mind that although I’m using Typemock Isolator the methods explained in this post would (probably) work with any Isolation framework.

Let me explain:

  1. Create a fake logger
  2. Make sure that this logger is returned from LogManager
  3. Create a new class and execute the method
  4. Make sure that Logger.Error was not called

When the test fails we get the following error:

image

It seems that Log.Error was called the only problem is that we have several Log.Error in our code and we can’t tell which of them was called. One solution would be to put break points on each of these calls an run the test using the debugger but we need a more elegant and simple solution.

Exceptions to the rescue

Finding out where the method was called seems a bit over specification – at least that’s what I thought when I was asked by a team member, thinking about this problem I realized that it’s a legitimate request to be able to know where where the calls that caused my test to fail.

A quick solution could be to use the Isolation framework’s exception throwing – when the offending method is called:

[TestFixture]
public class MyClassTests
{
[Test]
public void RunMethod_LogErrorNotInvoked()
{
// Arrange
var fakeLogger = Isolate.Fake.Instance();
Isolate.WhenCalled(() => fakeLogger.Error(null)).WillThrow(new Exception("Log.Error was called!"));
Isolate.WhenCalled(() => LogManager.GetLogger(typeof(MyClass))).WillReturn(fakeLogger);

// Act
var classUnderTest = new MyClass();
classUnderTest.RunMethod();

// Assert
Isolate.Verify.WasNotCalled(() => fakeLogger.Error(null));
}
}

I’ve added a call to WillThrow that would happen when the requested method is called.

Running the test shows exactly where the method was called (MyClass.cs, line 6)image

The only “downside” here is that I’m violating the test natural order – a reader expects to find some form of assert/Verify in the test – that’s why I’ve left the call to Verify at the nerd of the test, although it’s not needed because if the test fails it would throw an exception before reaching that line assertion at the end. I found it makes the test “readable” if you state what to expect from the test in code – but if you find it confusing or redundant you can remove it without changing the test.

One thought on “Find out where is the method that was called unexpectedly using fake objects

  1. Good catch. It could be a nice feature if fakelogger collected all calls together with stack traces, and Verify.WasNotCalled displayed all these.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.