Unit-testing – Should I Have One Interface Per Class For Unit Testing

mockingnetunit testing

Should I define an interface for every public behavior class (excluding data classes)?

I've spent many hours searching and reading to find a clear answer. If I search "Do you define an interface for every public class", 90% of the answers say you should not. However, I have yet to find any such comment explain how to do unit testing otherwise.

Some say Moq can mock concrete classes, but only for members that are declared virtual, and since the constructors cannot be marked virtual, there is no way for Moq to prevent code from running in the constructors (AFAIK). I have no interest in marking every member of every class as virtual.

It seems the answers to this question fall into 3 categories:

  • those who test everything
  • those who test only part of the code
  • those who don't bother with unit testing

I've seen all the arguments on both sides already, but I still haven't seen any other good way of doing extensive unit testing of the code. Why then, are 90% of the people advocating against this?

The way I'm doing it for now is to place the interface at the top of the same file within a #region, so there is no increase in files, I can easily navigate to the implementation, and it doesn't clutter the code view. If some interface needs to be implemented several times, nothing prevents me from moving it into separate files later.

One of the main reasons for creating such interface is because of limitations of mocking frameworks. Let's say the next version of .NET allowed mocking frameworks to mock non-virtual methods, should I still create those interfaces?

Taking a simple example, I have class A, B and C. A depends on IB and IC for testing. Even if not needed for mocking, A still needs instances of B and C injected via dependency injection. Using interfaces is optional for dependency injection but I have yet to see good examples recommending to not use interfaces. So in this hypothetical scenario of not being needed for mocking, should I still create those interfaces or not?

And finally, if creating such interfaces is a good approach (which many disagree with), is there any tool that can auto-generate those interfaces at compile-time so I don't have to copy the method signatures and comments all the time?

Best Answer

The underlying problem is the need for mocking in unit testing. Mocking is a code smell - it is unavoidable in some cases, but should not be pervasive in your unit tests.

Mocking is necessary for classes which are interfaces to external services, perform input/output, or are non-deterministic (time, random number generation). But you shouldn't need to mock every single class in your code. Doing this will lead to brittle tests, where every change in an interface will force you to update the corresponding tests mocks, which defeats the purpose of unit testing.

Mocking frameworks like Moq should IMHO only be used in the case of legacy codebases where you need to add tests to existing code which was not designed for testing. It should not be necessary in well-designed code.

To address the concern that a unit test should only test a single class: This is a misunderstanding. A unit test should test a unit of code which can be tested independently. This might be single function or a set of classes depending on how tightly coupled the code is. For example Math.Max() has no dependencies or side effects, so can be tested independently with no need for mocking. But we don't actually care if Max internally uses some helper classes. This is an implementation detail, as long as the public behavior corresponds to the specified requirements.

Ideally you want as much as your code to work like Max: A well defined interface with no public dependencies or side-effects. This have advantages in two ways:

  • You don't need any form of mocking to test Max.
  • If some other component depends on Max there is no need to mock it.

(On that token, let me dispel the myth that static methods are bad for testing. As Math.Max() shows, a static method without side-effects is actually the best thing ever for testing. Just avoid static state.)

Of course I/O and side-effects are necessary in any code. But you want to separate this from any complex logic which requires unit-testing.

I have class A, B and C. A depends on IB and IC for testing. Even if not needed for mocking, A still needs instances of B and C injected via dependency injection.

The problem with A B C examples is the answer depends on what A, B and C actually is. So lets take some examples:

A is InvoiceGenerator, B is Customer and C is an Order. Easy - you instantiate Customer and Order with data appropriate for the test. No need for mocking.

But if say A is InvoiceGeneratorAndSender and B is SmtpServer then you may temporarily need to mock SmtpServer, but it would be better to separate the invoice generation from the mailing. Invoice generation could then be tested without any need for mocking. The mailing would be a really shallow wrapper over the SmtpServer so wouldn't need to be unit tested at all. (But should be integration-tested.)