Is it recommended to mock concrete class?

19,310

Solution 1

In theory there is absolutely no problem mocking a concrete class; we are testing against a logical interface (rather than a keyword interface), and it does not matter whether that logical interface is provided by a class or interface.

In practice .NET/C# makes this a bit problematic. As you mentioned a .NET mocking framework I'm going to assume you're restricted to that.

In .NET/C# members are non-virtual by default, so any proxy-based methods of mocking behaviour (i.e. derive from the class, and override all the members to do test-specific stuff) will not work unless you explicitly mark the members as virtual. This leads to a problem: you are using an instance of a mocked class that is meant to be completely safe in your unit test (i.e. won't run any real code), but unless you have made sure everything is virtual you may end up with a mix of real and mocked code running (this can be especially problematic if there is constructor logic, which always runs, and is compounded if there are other concrete dependencies to be new'd up).

There are a few ways to work around this.

  • Use interfaces. This works and is what we advise in the NSubstitute documentation, but has the downside of potentially bloating your codebase with interfaces that may not actually be needed. Arguably if we find good abstractions in our code we'll naturally end up with neat, reusable interfaces we can test to. I haven't quite seen it pan out like that, but YMMV. :)
  • Diligently go around making everything virtual. An arguable downside to this is that we're suggesting all these members are intended to be extension points in our design, when we really just want to change the behaviour of the whole class for testing. It also doesn't stop constructor logic running, nor does it help if the concrete class requires other dependencies.
  • Use assembly re-writing via something like the Virtuosity add-in for Fody, which you can use to modify all class members in your assembly to be virtual.
  • Use a non-proxy based mocking library like TypeMock (paid), JustMock (paid), Microsoft Fakes (requires VS Ultimate/Enterprise, though its predecessor, Microsoft Moles, is free) or Prig (free + open source). I believe these are able to mock all aspects of classes, as well as static members.

A common complaint lodged against the last idea is that you are testing via a "fake" seam; we are going outside the mechanisms normally used for extending code to change the behaviour of our code. Needing to go outside these mechanisms could indicate rigidity in our design. I understand this argument, but I've seen cases where the noise of creating another interface/s outweighs the benefits. I guess it's a matter of being aware of the potential design issue; if you don't need that feedback from the tests to highlight design rigidity then they're great solutions.

A final idea I'll throw out there is to play around with changing the size of the units in our tests. Typically we have a single class as a unit. If we have a number of cohesive classes as our unit, and have interfaces acting as a well-defined boundary around that component, then we can avoid having to mock as many classes and instead just mock over a more stable boundary. This can make our tests a more complicated, with the advantage that we're testing a cohesive unit of functionality and being encouraged to develop solid interfaces around that unit.

Hope this helps.

Solution 2

Update:

3 years later I want to admit that I changed my mind.

In theory I still do not like to create interfaces just to facilitate creation of mock objects. In practice ( I am using NSubstitute) it is much easier to use Substitute.For<MyInterface>() rather than mock a real class with multiple parameters, e.g. Substitute.For<MyCLass>(mockedParam1, mockedParam2, mockedParam3), where each parameter should be mocked separately. Other potential troubles are described in NSubstitute documentation

In our company the recommended practice now is to use interfaces.

Original answer:

If you don't have a requirement to create multiple implementations of the same abstraction, do not create an interface.   As it pointed by David Tchepak, you don't want to bloating your codebase with interfaces that may not actually be needed.

From http://blog.ploeh.dk/2010/12/02/InterfacesAreNotAbstractions.aspx

Do you extract interfaces from your classes to enable loose coupling? If so, you probably have a 1:1 relationship between your interfaces and the concrete classes that implement them. That’s probably not a good sign, and violates the Reused Abstractions Principle (RAP).

Having only one implementation of a given interface is a code smell.

If your target is the testability, i prefer  the second option from David Tchepak's answer above.

However I am not convinced that you have to make everything virtual. It's sufficient to make virtual only the methods, that you are going to substitute. I also will add a comment next to the method declaration that method is virtual only to make it substitutable for unit test mocking.

However note that substitution of concrete classes instead of interfaces has some limitations. E.g. for NSubstitute

Note: Recursive substitutes will not be created for classes, as creating and using classes can have potentially unwanted side-effects

.

Solution 3

The question is rather: Why not?

I can think of a couple of scenarios where this is useful, like:

Implementation of a concrete class is not yet complete, or the guy who did it is unreliable. So I mock the class as it is specified and test my code against it.

It can also be useful to mock classes that do things like database access. If you don't have a test database you might want to return values for your tests that are always constant (which is easy by mocking the class).

Share:
19,310
DonDon
Author by

DonDon

var brain = string.Empty;

Updated on June 06, 2022

Comments

  • DonDon
    DonDon almost 2 years

    Most of the examples given in mocking framework website is to mock Interface. Let say NSubstitute that I'm currently using, all their mocking examples is to mock interface.

    But in reality, I saw some developer mock concrete class instead. Is it recommended to mock concrete class?

  • Simon
    Simon almost 12 years
    David. I assume you are talking about this addin for Fody github.com/SimonCropp/Virtuosity ? if so technically you cant "use as part of your test project". To change an assemblies members to be virtual you would have to apply Fody to the assembly you want to change. The reason is that Fody works at compile time, not at assembly load time. What I am getting at is that in this scenario you cant "use fody without altering what you intend to deploy".
  • David Tchepak
    David Tchepak almost 12 years
    Thanks @Simon; answer updated (please edit it further if required). Can it be set up to only modify the assembly referenced by and copied into the test project?
  • DonDon
    DonDon almost 11 years
    a very detailed answer. Thanks :)
  • Ruskin
    Ruskin almost 9 years
    I am a strong believer in not changing code to make it more testable. Making methods virtual has the same effect as adding interfaces to classes. Which looks cleaner? To me interfaces do. Interfaces also avoid calling ctor logic which sometimes can be very complicated. So IMO a 1:1 interface/concrete class relationship isn't a "code-smell".
  • DevNewb
    DevNewb about 8 years
    Hi David, great answer! I don't quite understand how "Needing to go outside these mechanisms could indicate rigidity in our design." Wouldn't it be equally undesirable to change code design just to use a simpler tool for tests? Could you maybe elaborate on that, please? Sorry I know it's been over 3 years :)
  • David Tchepak
    David Tchepak about 8 years
    @DevNewb: If a class uses an interface, we should be able to substitute any implementation of that interface. This design gives us some flexibility: one implementation can call a DB, another can call a web service, another can be a test double. If we are changing behaviour for testing via the profiler API, I think we are unlikely to also use this in production code to support multiple behaviours. I'm not trying to judge "desirability", just an observation about how one mechanism can serve different purposes.
  • Chris Marisic
    Chris Marisic about 8 years
    @Ruskin you're mistaken. Making a method virtual for testing is a legitimate purpose. You are replacing its implementation. The fact you want to do it for testing shows it is equally valid for other purposes. You however are being very explicit in this choice. It also is a great sanity check to ensure you're not violating the Open Closed principle and have a leaky abstraction.
  • Chris Marisic
    Chris Marisic about 8 years
    Single implementation interfaces are a far greater evil.