Is it recommended to mock concrete class?

≡放荡痞女 提交于 2019-11-26 22:20:13

问题


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?


回答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.




回答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

.




回答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).




回答4:


Its not that it is recommended, it's that you can do this if you have no other choice.

Usually well designed project rely on defining interfaces for your separate components so you can tests each of them in isolation by mocking the other ones. But if you are working with legacy code /code that you are not allowed to change and still want to test your classes then you have no choice and you cannot be criticized for it (assuming you made the effort to try to switch these components to interfaces and were denied the right to).



来源:https://stackoverflow.com/questions/12174304/is-it-recommended-to-mock-concrete-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!