How do I mock a class without an interface?
Asked Answered
S

9

127

I am working on .NET 4.0 using C# in Windows 7.

I want to test the communication between some methods using mock. The only problem is that I want to do it without implementing an interface. Is that possible?

I just read a lot of topics and some tutorials about mock objects, but all of them used to mock interfaces, and not the classes. I tried to use Rhino and Moq frameworks.

Sukhum answered 5/12, 2013 at 13:1 Comment(4)
It really bites that these tools are created from the perspective of using "IInterfaces" exclusively.Pernell
There are created assuming that you are using interface based DI. This is a pretty standard pattern these days.Pajamas
Unfortunately, that pattern conflicts with the Immutable Type "pattern" :(Similitude
There are multiple methods not mentioned here in this answerTranscontinental
J
99

Simply mark any method you need to fake as virtual (and not private). Then you will be able to create a fake that can override the method.

If you use new Mock<Type> and you don't have a parameterless constructor then you can pass the parameters as the arguments of the above call as it takes a type of param Objects

Joaquinajoash answered 5/12, 2013 at 13:3 Comment(5)
This leaves me wonder if it is necessary at all to create an interface for every class I want to mock. Couldn't we just use concrete classes for mocking if they don't have an interface?Uella
@Uella In fact, I tend to create a class first, only creating an interface if I need to break out common functionality.Joaquinajoash
How do you then pass the mock to an object expecting the specified type? If I create a mock 'new Mock<MyType>' and try to pass it to an object expecting MyType i get error "Cannot convert Mock<MyType> to MyType".Z
I worked it out, if you define your mock as 'var myMock = new Mock<MyType>()' you must pass it to the class that uses the mock as myMock.Object.Z
The main problem arises when there is no parameterless constructor and the parameterized constructor in the closed Concrete class is not public. (Here, by 'closed' I mean, in an external package). Then, one can't implement an interface, can't make that method virtual & also can't use the parameterized constructor.Wellknit
B
31

Most mocking frameworks (Moq and RhinoMocks included) generate proxy classes as a substitute for your mocked class, and override the virtual methods with behavior that you define. Because of this, you can only mock interfaces, or virtual methods on concrete or abstract classes. Additionally, if you're mocking a concrete class, you almost always need to provide a parameterless constructor so that the mocking framework knows how to instantiate the class.

Why the aversion to creating interfaces in your code?

Blackcock answered 5/12, 2013 at 14:22 Comment(10)
Because it clogs up the codebase with tons of interfaces which, if not for some technical limitation of testing frameworks, would be completely unnecessary?Transcontinental
@BlueRaja-DannyPflughoeft Which holds equally for marking methods virtual, too. =/ Heck, I'm looking at a case where I could just make the entire class in question static if I wasn't thinking about mocking it. (It's just a collection of methods. It has no state and never will.)Sigler
You've heard the expression "reach exceeds grasp". That explains why developers are always complaining. The C# language was not designed with unit testing in mind. That's why it's really hard to inject mock dependencies without a tremendous clutter of pointless interfaces or virtual methods. Perhaps someone will soon invent a language that is easy to unit-test. By then we'll have something else to complain about.Debonair
I'm glad to know I'm not the only one that views interfaces and virtual methods as clutter if their only purpose is to serve tests.Chigoe
"only purpose is to serve tests" but isn't creating testable code important to you?Embolic
"Why the aversion to creating interfaces in your code?" Because I didn't write this object, and I don't have access to the code. I need to create a mock of a closed object I don't own which does not implement an interface.Intransigent
You could write a subclass or proxy class yourself that implements an interface you control yourself. Now the external dependency has just become an implementation detail. (E.g. see answer by Anang Satria)Catchall
@bouke That's great, and I've actually done that. However, this just pushes off the issue one level. Now I need to unit test the proxy class, which again contains an object I need to mock, which does not implement an interface. So back to the original question, how do I unit test this?Intransigent
@MakkyNZ, creating testable code is important, but what about OOP and KISS?Anemometer
@Embolic Good point but the idea is: can I get testability without affecting maintainability.Harrell
W
30

With MoQ, you can mock concrete classes:

var mocked = new Mock<MyConcreteClass>();

but this allows you to override virtual code (methods and properties).

Wagshul answered 5/12, 2013 at 13:12 Comment(6)
i tried that, but when i run my test project, my program throws an exception: "Can not instantiate proxy of class" "Could not find a parameterless constructor."Sukhum
Just pass the constructor parameters to the Mock<> constructor. E.g. new Mock<MyConcreteClass>(param1, anotherParam, thirdParam, evenMoreParams);Belfort
Why not just create an interface for the class?Fogbound
@RobertPerry because interfaces just for the sake of unit testing is misusing interfaces.Unhealthy
@KieranDevlin - Should you not be writing code with an interface-first approach anyway? I'm not sure I understand your commentFogbound
@RobertPerry This answer specified that you can create mock objects from concrete types. You commented asking why not just create an interface for the class on a question that is about mocking without an interface. My comment is as it stands, I don't know how any clearer I can be? Putting an interface on your type for the sole reason of being able to mock the interface is misusing interfaces. That is why you should not simply "just create an interface for the class".Unhealthy
L
26

I think it's better to create an interface for that class. And create a unit test using interface.

If it you don't have access to that class, you can create an adapter for that class.

For example:

public class RealClass
{
    int DoSomething(string input)
    {
        // real implementation here
    }
}

public interface IRealClassAdapter
{
    int DoSomething(string input);
}

public class RealClassAdapter : IRealClassAdapter
{
    readonly RealClass _realClass;

    public RealClassAdapter() => _realClass = new RealClass();

    int DoSomething(string input) => _realClass.DoSomething(input);
}

This way, you can easily create mock for your class using IRealClassAdapter.

Hope it works.

Larios answered 24/7, 2017 at 10:40 Comment(6)
This is terrible for maintenance. If you want to add a new method to the RealClass, you must also add it to the IReadClassAdapter and also to RealClassAdapter. TRIPLE effort! Better solution is to just add "virtual" keyword to each public method in the RealClass.Debonair
@JohnHenckel I'm assuming that we don't have access to RealClass. So adding "virtual" method is not an option in my opinion. If you have access to real class it's better to implement the interface directly to the new class, I think that's the best practice.Larios
Thanks. This seems to be the one legit solution to mocking classes with no access. Even it's lot's of "stupid" code, i need this to run the code in an "inappropriate environment"Equipment
@JohnHenckel, wouldn't you actually want the tests to reflect on the blast radius of your changes down the road? In a way mocks hide such, while stubs do not. Maybe, precisely, the change should not be easy if it is too impactful, sort of raising an alarm to be cautious.Cooley
So in my case, the class I needed to mock was from a thrid party library and thus not a class I actually wanted to test. I ended up rewriting my code, moving the use of the third party class some levels up, allowing me to test the code that uses the result. Doing this saved me from the potential maintenance nightmare that is mentioned here, got me cleaner code and allowed me to create a test that does not even require any mocks.Faultfinder
+1 for this for mocking data without access to the class - As you mentioned a virtual method is not an option in a lot of cases and is needed to mock some SQL data from a static class in a separate library. This solution helps keep the implementation in one place and simply just need to update an interface if any changes are needed.Oleograph
P
9

If you cannot change the class under test, then the only option I can suggest is using MS Fakes https://msdn.microsoft.com/en-us/library/hh549175.aspx. However, MS Fakes works only in a few editions of Visual Studio.

Polito answered 30/3, 2017 at 8:5 Comment(0)
A
7

The standard mocking frameworks are creating proxy classes. This is the reason why they are technically limited to interfaces and virtual methods.

If you want to mock 'normal' methods as well, you need a tool that works with instrumentation instead of proxy generation. E.g. MS Moles and Typemock can do that. But the former has a horrible 'API', and the latter is commercial.

Actinism answered 16/12, 2013 at 17:12 Comment(1)
can we mock the normal class with that public method ?Capsize
A
5

If worse comes to worse, you can create an interface and adapter pair. You would change all uses of ConcreteClass to use the interface instead, and always pass the adapter instead of the concrete class in production code.

The adapter implements the interface, so the mock can also implement the interface.

It's more scaffolding than just making a method virtual or just adding an interface, but if you don't have access to the source for the concrete class it can get you out of a bind.

Aerobe answered 3/8, 2017 at 23:41 Comment(0)
S
3

It is a bit old question but nevertheless. There are powerful mocking frameworks these days that are capable of mocking concrete classes like JustMock and Typemock.

Spartan answered 29/10, 2021 at 12:1 Comment(2)
Thank you. Let's get past the argument where we expect everyone to use the same design patterns and just support ALL design patterns. Free language leads to free thought.Overspill
Can also add github.com/ZeroMock/ZeroMock to this listGadson
C
2

I faced something like that in one of the old and legacy projects that i worked in that not contains any interfaces or best practice and also it's too hard to enforce them build things again or refactoring the code due to the maturity of the project business, So in my UnitTest project i used to create a Wrapper over the classes that I want to mock and that wrapper implement interface which contains all my needed methods that I want to setup and work with, Now I can mock the wrapper instead of the real class.

For Example:

Service you want to test which not contains virtual methods or implement interface

public class ServiceA{

public void A(){}

public String B(){}

}

Wrapper to moq

public class ServiceAWrapper : IServiceAWrapper{

public void A(){}

public String B(){}

}

The Wrapper Interface

public interface IServiceAWrapper{

void A();

String B();

}

In the unit test you can now mock the wrapper:

    public void A_Run_ChangeStateOfX()
    {
    var moq = new Mock<IServiceAWrapper>();
    moq.Setup(...);
    }

This might be not the best practice, but if your project rules force you in this way, do it. Also Put all your Wrappers inside your Unit Test project or Helper project specified only for the unit tests in order to not overload the project with unneeded wrappers or adaptors.

Update: This answer from more than a year but in this year i faced a lot of similar scenarios with different solutions. For example it's so easy to use Microsoft Fake Framework to create mocks, fakes and stubs and even test private and protected methods without any interfaces. You can read: https://learn.microsoft.com/en-us/visualstudio/test/isolating-code-under-test-with-microsoft-fakes?view=vs-2017

Cassius answered 13/12, 2017 at 13:9 Comment(1)
I think it's important to note that Microsoft Fakes is only available with Visual Studio Enterprise and thus, not only is out of reach of many developers, but also causes issues with several CI systems.Willawillabella

© 2022 - 2024 — McMap. All rights reserved.