11

I was reading some blog posts this morning, and stumbled across this one:

If the only class that ever implements the Customer interface is CustomerImpl, you don't really have polymorphism and substitutability because there is nothing in practice to substitute at runtime. It's fake generality.

That makes sense to me, as implementing an interface adds complexity and, if there is only ever one implementation, one might argue that it adds needless complexity. Writing code that is more abstract than it needs to be is often considered to be code smell called "speculative generality" (also mentioned in the post).

But, if I'm following TDD, I can't (easily) create test doubles without that speculative generality, whether in the form of interface implementation or our other polymorphic option, making the class inheritable and its methods virtual.

So, how do we reconcile this tradeoff? Is it worth it to be speculatively general to facilitate testing/TDD? If you're using test doubles, do these count as second implementations and thus make the generality no longer speculative? Should you consider a more heavyweight mocking framework that allows mocking of concrete collaborators (e.g. Moles versus Moq in the C# world)? Or, should you test with the concrete classes and write what could be considered "integration" tests until such time as your design naturally requires polymorphism?

I'm curious to read other people's takes on this matter -- thanks in advance for your opinions.

6
  • Personally I think entities shouldn't be mocked. I only mock services, and those need an interface in any case, because the code domain code typically has no reference to the code where services are implemented. Feb 5, 2012 at 22:46
  • 7
    We users of dynamically typed languages laugh at your chaffing at the chains your statically typed languages have put on you. This is one thing that makes unit testing easier in dynamically typed languages, I don't have to have developed an interface in order to subtitute an object for testing purposes. Feb 6, 2012 at 4:49
  • Interfaces are not only used to effect generality. They are used for many purposes, decoupling of your code being one of the important ones. Which in turn makes testing of your code a heck of a lot easier. Feb 6, 2012 at 8:24
  • @WinstonEwert That's an interesting benefit of dynamic typing that I hadn't considered before as someone who, as you point out, doesn't typically work in dynamically typed languages. Feb 6, 2012 at 15:46
  • @CodeInChaos I hadn't considered the distinction for the purposes of this question, though it is a reasonable distinction to make. In this case, we might think of service/framework classes with only one (current) implementation. Let's say I have a database that I access with DAOs. Should I not interface the DAOs until I have a secondary persistence model? (That seems to be what the blog post author is implying) Feb 6, 2012 at 15:54

5 Answers 5

14

I went and read the blog post, and I agree with a lot of what the author said. However, if you're writing your code using interfaces for unit testing purposes, I'd say that the mock implementation of the interface is your second implementation. I'd argue that it really doesn't add much in the way of complexity to your code, especially if the trade-off of not doing so results in your classes being tightly coupled and hard to test.

4
  • 3
    Absolutely right. Testing code is part of the application because you need it to get the design, implementation, maintenance etc. right. The fact that you don't ship it to the customer is irrelevant - if there is a second implementation in your test suite, then the generality is there and you should accomodate it. Feb 6, 2012 at 7:40
  • 1
    This is the answer that I find the most convincing (and @KilianFoth adding that whether or not the code ships a second implementation still exists). I'm going to hold off on accepting the answer a bit to see if anyone else chimes in, though. Feb 6, 2012 at 17:39
  • I would also add, when you are depending on the interfaces in the tests, the generality is no longer speculative
    – Pete
    Feb 1, 2013 at 17:39
  • "Not doing so" (using interfaces) doesn't automatically result in your classes being tightly-coupled. It just doesn't. E.g. in the .NET Framework, there is a Stream class, but there's no tight-coupling. Dec 14, 2015 at 11:53
3

Testing code in general isn't easy. If it were, we'd have been doing it all a long time ago, and not making such a bit deal of it only in the last 10-15 years. One of the biggest difficulties has always been in determining how to test code that has been written cohesively, and well factored, and testable without breaking encapsulation. The BDD principal suggests we focus almost entirely on behavior, and in some ways seems to suggest that you don't really need to worry about the inner details to such a large degree, but this can often make things quite difficult to test if there are a lot of private methods that do "stuff" in a very hidden way, as it can increase the overall complexity of your test to deal with all the possible outcomes at a more public level.

Mocking can help to a certain extent, but again is fairly externally focused. Dependency Injection can also work quite nicely, again with mocks or test doubles, but this can also require that you expose elements either via an interface, or directly, that you might have otherwise preferred to remain hidden - this is particularly true if you wish to have a nice paranoid level of security about certain classes within your system.

For me, the jury is still out about whether to design your classes to be more easily testable. This can create problems if you find yourself needing to provide new tests while maintaining legacy code. I accept that you should be able to test absolutely everything in a system, yet I don't like the idea of exposing - even indirectly - the private internals of a class, just so that I can write a test for them.

For me, the solution has always been to take a fairly pragmatic approach, and combine a number of techniques to suit each specific situation. I use a lot of inherited test doubles to expose inner properties and behaviors for my tests. I mock everything that can be attached to my classes, and where it won't compromise the security of my classes, I'll provide a means to override or inject behaviors for the purposes of testing. I'll even consider providing a more event-driven interface if it will help improve the ability to test code

Where I find any "untestable" code, I look to see if I can refactor to make things more testable. Where you have a lot of private code doing hidden behind the scenes stuff, often you'll find new classes waiting to be broken out. These classes might be used internally, but can often be tested independently with less private behaviors, and often subsequently less layers of access and complexity. One thing I do take care to avoid however is writing production code with test code built in. It can be tempting to create "test lugs" that result in including such horrors as if testing then ..., which indicates a testing problem not fully deconstructed and incompletely solved.

You might find it helpful to read Gerard Meszaros' xUnit Test Patterns book, which covers all of this sort of stuff in much greater detail than I can go into here. I probably don't do everything he suggests, but it does help to clarify some of the trickier test situations to deal with. At the end of the day, you want to be able to satisfy your testing requirements while still applying your preferred designs, and it helps to have a better understanding of all of the options in order to better to decide where you might need to compromise.

1

Does the language you use have a way to "mock" an object for testing? If so these annoying interfaces can go away.

On a different note there can be reasons to have a SimpleInterface and a sole ComplexThing that implements it. There could be pieces of the ComplexThing that you don't want accessible to the SimpleInterface user. It's not always because of an over-exuberant OO-ish coder.

I'll step away now and let everyone jump on the fact that code that does this "smells bad" to them.

1
  • Yes, I work in language(s) with mocking frameworks that support mocking concrete objects. These tools require varying degrees of jumping through hoops to do it. Feb 6, 2012 at 15:43
0

I'll answer in two parts:

  1. You don't need interfaces if you're only are interested in testing. I use mocking frameworks for that purpose (in Java: Mockito or easymock). I believe that the code you design should not be modified for testing purposes. Writing testable code, is equivalent of writing modular code, so I do tend to write modular (testable) code and test only the code public interfaces.

  2. I've been working in a big Java project and I'm becoming deeply convinced that using read-only (only getters) interfaces is the way to go (please note that I'm a big fan of immutability). The implementing class may have setters, but that's an implementation detail that should not be exposed to outer layers. From another perspective I do prefer composition over inheritance (modularity, remember?), so interfaces also help here. I'm willing to pay the cost of speculative generallity than shooting my self in the foot.

0

I have seen many advantages since I started programming more to an interface beyond polymorphism.

  • It forces me to think harder about the interface of the class (it's public methods) beforehand and how it will interact with other classes' interfaces.
  • It helps me write smaller classes that are more cohesive and follow the single responsibility principal.
  • It is easier to test my code
  • Less static classes / global state as the class must be instance-level
  • Easier to integrate and assembly pieces together before the whole program is ready
  • Dependency injection, separating object construction from the business logic

Many people will agree that more, smaller classes are better than fewer, bigger classes. You don't have to focus on as much at one time and each class has a well-defined purpose. Other's may say you are adding to the complexity by having more classes.

It is good to use tools to improve productivity, but I think relying solely on Mock frameworks and such in place of building testibility and modularity directly into the code will result in lower quality code in the long run.

All in all, I believe it has helped me write higher quality code and the benefits far outweigh any consequences.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.