Sometimes you come across a class that you need to stub or mock but it does not have an interface. The easiest way is of course to add an interface (duh) but that might not always be possible. Here are three methods you can use to stub or mock a class without an interface.
Note: I will use the words stub and mock interchangeably in this post, even though you might consider these different test double strategies.
Using virtual
If you are in luck, the class that you want to mock has defined its methods as virtual. This makes it possible for you to inherit the class and then override their implementation. For example if we have the class MyClass
with the dependency MyDependency
:
public class MyClass
{
private MyDependency _myDependency;
public MyClass(MyDependency myDependency)
{
_myDependency = myDependency;
}
public string Get()
{
return _myDependency.Get();
}
}
public class MyDependency
{
public virtual string Get() //virtual!
{
return "This is my dependency";
}
}
MyClass
takes a MyDependency
and calls the Get()
method on MyDependency when its own Get()
method is called - basically forwarding the call and returning a string. In the above you will notice that the Get()
method of MyDependency
is virtual. This makes it possible for us to override it if we inherit MyDependency
in our own stub implementation like below:
public class MyVirtualDependencyStub : MyDependency
{
public override string Get()
{
return "This is an override";
}
}
If we set up test cases for this we will see that it works with both the old implementation and the stub:
[Test]
public void WorksWithMyDependency()
{
var myDepdendency = new MyDependency(); //original
var myClass = new MyClass(myDepdendency);
Assert.AreEqual("This is my dependency", myClass.Get());
}
[Test]
public void WorksWithMyVirtualDependencyStub()
{
var myDepdendency = new MyVirtualDependencyStub(); //Virtual implementation
var myClass = new MyClass(myDepdendency);
Assert.AreEqual("This is an override", myClass.Get());
}
The first test shows that the original implementation returns "This is my dependency" and the implementation where we have overridden the Get()
method returns "This is an override".
This also works with mocking frameworks. Making a lot of stubs for your tests can make your project cluttered. I use NSubstitute for mocking, but most mocking frameworks can do something like the following on classes with virtual methods:
[Test]
public void WorksWithNsubstitute()
{
var myDepdendency = Substitute.For<MyDependency>();
myDepdendency.Get().Returns("This is an override");
var myClass = new MyClass(myDepdendency);
Assert.AreEqual("This is an override", myClass.Get());
}
That is all there is to stubbing or mocking a class using virtual and override. If you are wondering if you can use the new
keyword instead, please read the following on the difference between virtual
/override
and new
keywords.
Make a wrapper class
If the class you want to mock does not have an interface or virtual methods then you can wrap it in another class that does have an interface. We will use the same example as before but without the virtual
keyword on the Get()
method:
public class MyClass
{
private MyDependency _myDependency;
public MyClass(MyDependency myDependency)
{
_myDependency = myDependency;
}
public string Get()
{
return _myDependency.Get();
}
}
public class MyDependency
{
public string Get() //Not virtual!
{
return "This is my dependency";
}
}
First we will introduce a Wrapper class with an interface like the below:
public interface IMyDependency
{
public string Get();
}
public class MyDependencyWrapper : IMyDependency
{
private readonly MyDependency _myDependency;
public MyDependencyWrapper(MyDependency myDependency)
{
_myDependency = myDependency;
}
public string Get()
{
return _myDependency.Get();
}
}
We can then change the original implementation to use the IMyDependency
interface rather than the original MyDependency
implementation:
public class MyClass
{
private IMyDependency _myDependency;
public MyClass(IMyDependency myDependency) //Now using IMyDependency
{
_myDependency = myDependency;
}
public string Get()
{
return _myDependency.Get();
}
}
When using this new wrapper your original code will have to change as it will now have to use the interface we just created. It also means you need to use the wrapper in your production code, I believe very few developers find this "pretty" but it is a way forward. Below I have demonstrated the use of the wrapper in a test, just to show that we need to use the wrapper whenever we want to use the original implementation:
[Test]
public void WorksWithMyDependencyWrapper()
{
var myDependencyImplementation = new MyDependency();
var myDepdendency = new MyDependencyWrapper(myDependencyImplementation);
var myClass = new MyClass(myDepdendency);
Assert.AreEqual("This is my dependency", myClass.Get());
}
Now to what we wanted to do, we can create a simple stub and use it within our test to mock MyDependency:
public class MyDependencyStub : IMyDependency
{
public string Get()
{
return "This is a stub";
}
}
[Test]
public void WorksWithMyDependencyStub()
{
var myDepdendency = new MyDependencyStub();
var myClass = new MyClass(myDepdendency);
Assert.AreEqual("This is a stub", myClass.Get());
}
The test code is simple stubbing using an interface, if you are used to this, this will seem very familiar to you. As an alternative to using a stub, you can use your favourite mocking framework now that you have an interface, I like NSubstitute:
[Test]
public void WorksWithNsubstitute()
{
var myDepdendency = Substitute.For<IMyDependency>();
myDepdendency.Get().Returns("This is a stub from NSubstitute");
var myClass = new MyClass(myDepdendency);
Assert.AreEqual("This is a stub from NSubstitute", myClass.Get());
}
In the above we create a mock that returns "This is a stub from NSubstitute", we then use this as our dependency instead.
Using the ProxyInterfaceSourceGenerator
The previous example contains some boilerplate for wrapping the class, instead of writing it yourself you can use the ProxyInterfaceSourceGenerator nuget package. Given the class you want to mock it can create a proxy (wrapper) of it that you can use instead.
We will use the same example as before:
public class MyClass
{
private MyDependency _myDependency;
public MyClass(MyDependency myDependency)
{
_myDependency = myDependency;
}
public string Get()
{
return _myDependency.Get();
}
}
public class MyDependency
{
public string Get()
{
return "This is my dependency";
}
}
Using the ProxyInterfaceGenerator we can create a partial interface called IMyDependency
. We only have to decorate it with the ProxyInterfaceGenerator.Proxy
attribute and the type of our class:
[ProxyInterfaceGenerator.Proxy(typeof(MyDependency))]
public partial interface IMyDependency
{
}
As before we need to change MyClass to use the interface (IMyDependency
) as a dependency rather than the implementation (MyDependency
):
public class MyClass
{
private IMyDependency _myDependency;
public MyClass(IMyDependency myDependency)
{
_myDependency = myDependency;
}
public string Get()
{
return _myDependency.Get();
}
}
Without having to define the methods of the interface - as the proxy does this behind the scenes - we can create a stub and a test like before:
public class MyDependencyStub : IMyDependency
{
public string Get()
{
return "This is a stub";
}
}
[Test]
public void WorksWithMyDependencyStub()
{
var myDepdendency = new MyDependencyStub();
var myClass = new MyClass(myDepdendency);
Assert.AreEqual("This is a stub", myClass.Get());
}
Just as the previous example, you will have to change your original code. You will have to wrap the class you wanted to mock in the proxy:
[Test]
public void OriginalCodeWorksWithProxy()
{
var myDependencyImplementation = new MyDependency();
var myDepdendency = new MyDependencyProxy(myDependencyImplementation);
var myClass = new MyClass(myDepdendency);
Assert.AreEqual("This is my dependency", myClass.Get());
}
This approach is similar to the previous one, but you do not have to create the wrapper class yourself!
That is all
I hope you found this worthwhile. If you know a fourth and better way, please let me know in the comments down below, I would like to cover that as well!