Fake It Easy Non Virtual Methods Cannot Be Intercepted

Introduction

Usually I use interfaces for my unit tests. This is a good practice, and should be followed when possible. But, last week I was testing some legacy code, which had no interfaces and stumbled with something: I was trying to mock a class with a non-virtual method and saw that the original method was being executed, instead of doing nothing (the expected for the mock). I was using NSubstitute, but the same principles apply to most of the more popular mocking frameworks, like Moq, FakeItEasy or Rhino mocks. All of them use for their backend Castle DynamicProxy to create a proxy around the object and do its magic. All of them state that they can only mock virtual methods, so I should be aware that I could not mock non-virtual methods.

This would be ok if the class to be mocked is yours, just add theVirtual keyword to your method and you're done. You'll have some size and performance penalty in exchange for your flexibility. Or you could use something like Typemock Isolator (this is a great mocking framework that can mock almost anything)- that would work even if you don't have access to the source code. But why aren't non-virtual methods mocked ?

Let's start with an example. Let's say I have the code of the class below:

public class ClassNonVirtualMehods {     public void NonVirtualMethod()     {          Console.WriteLine("Executing Non Virtual Method");     }      public virtual void VirtualMethod()     {         Console.WriteLine("Executing Virtual Method");     } }

My class to test receives an instance of this class:

public class ClassToTest {     public void CallVirtualMethod(ClassNonVirtualMehods param)     {         param.VirtualMethod();      }      public void CallNonVirtualMethod(ClassNonVirtualMehods param)     {         param.NonVirtualMethod();     } }

My tests are (I'm using NSubstitute here):

[TestClass] public class UnitTest1 {     [TestMethod]     public void TestVirtualMethod()     {         var mock = Substitute.For<ClassNonVirtualMehods>();         var sut = new ClassToTest();         sut.CallVirtualMethod(mock);     }      [TestMethod]     public void TestNonVirtualMethod()     {         var mock = Substitute.For<ClassNonVirtualMehods>();         var sut = new ClassToTest();         sut.CallNonVirtualMethod(mock);     } }

If you run these tests, you'll see this:

You'll see that the non virtual method is executed (writing to the console), while the virtual method is not executed, as we expected.

Why aren't non-virtual methods mocked ?

To explain that, we should see how Castle DynamicProxy works and how C# polymorphism works.

From here, you can see that DynamicProxy works with two types of proxies: Inheritance-based and composition-based. In order to put in place their mocks, the mock frameworks use the inheritance-based proxy, and that would be the same as create an inherited class (the mock) that overrides the methods and put the desired behavior in them. It would be something like this:

public class MockClassWithNonVirtualMethods : ClassNonVirtualMehods {     public override void VirtualMethod()     {         // Don't do anything     } }

In this case, as you can see, you can only override virtual methods, so the non-virtual methods will continue to execute the same way. If you have tests like this ones, you will see the same behavior as you did with NSubstitute:

You could use the composition-based proxy, but when you read the documentation, you'll see that this is not an option:

Class proxy with target – this proxy kind targets classes. It is not a perfect proxy and if the class has non-virtual methods or public fields these can't be intercepted giving users of the proxy inconsistent view of the state of the object. Because of that, it should be used with caution.

Workaround for non-virtual methods

Now we know why mocking frameworks don't mock non-virtual methods, we must find a workaround.

My first option was to use the RealProxy class to create the mock and intercept the calls, like shown in my article in the MSDN Magazine. This is a nice and elegant solution, as the impact to the source code would be minimum, just create a generic class to create the proxy and use it instead of the real class.  But it turned on that the RealProxy has two disadvantages:

  • It can only create proxies that inherit of MarshalByRefObject or are interfaces, and our classes are none of them
  • It isn't available in .NET Core, and it was replaced by DispatchProxy, which is different, and can only proxy interfaces

So, I discarded this option and went for another option. I was looking to change the IL Code at runtime, using Reflection.Emit, but this is prone to errors and not an easy task, so I kept searching until I found Fody. This is a very interesting framework to allow you to add new code at runtime. This is the same thing I was expecting to do, with the difference that the solutions are ready and tested. Just add an add-in and you're set – the task you are trying to do is there. There are a lot of add-ins for it and it's very simple to use them. If you don't find what you are trying to do there, you can develop your own add-in. In our case, there is already an add-in, Virtuosity.

To use it, all we have to do is to install the packages Fody and Fody.Virtuosity to your project, add a xml file namedFodyWeavers.xml with this content:

<?xml version="1.0" encoding="utf-8"?> <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">   <Virtuosity /> </Weavers>

Set its Build Action toNone and Copy to Output Directory toCopy If Newer and you're set. This will transform all your non-virtual methods to virtual and you will be able to mock them in any mocking framework, with no need to change anything else in your projects.

Conclusion

The procedure described here is nothing something that I would recommend doing on a daily basis, but when you have some legacy code to test, where changes to the source code are difficult to make, it can be a lifesaver. When you have the time and the means, you should develop an interface and use it in your code, using Dependency Injection. That way, you will reduce coupling and enhance its maintainability.

All the source code for this article is at https://github.com/bsonnino/MockNonVirtual

ortegaprike1986.blogspot.com

Source: https://blogs.msmvps.com/bsonnino/2020/10/04/mocking-non-virtual-methods-of-a-class/

0 Response to "Fake It Easy Non Virtual Methods Cannot Be Intercepted"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel