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<ILog>();
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<ILog>();
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.

Labels: , , , , ,