5

Maybe I'm showing my lack of understanding of dependency injection and testing, but I don't understand how using dependency injection with classes that don't implement interfaces helps me at all with testing?

For instance, in the Enterprise Library 5.0 documentation it talks about using the Unity container to create instances. It says that this aids "testability: It is trivial to isolate classes from dependencies when using the dependency injection style." MSDN

How do I use this in my unit testing fixtures? Their example has a constructor with parameters as classes rather than interfaces:

public class TaxCalculator 
{
  private ExceptionManager _exceptionManager;
  private LogWriter _logWriter;

  public TaxCalculator(ExceptionManager em, LogWriter lw) 
  {
    this._exceptionManager = em;
    this._logWriter = lw;
  }
}
Bob Wintemberg
  • 3,212
  • 6
  • 34
  • 44

3 Answers3

9

To answer the question "How do I test Enterprise Library code": you don't. Testing other people's stuff is the job of other people. Any interfaces or abstractions in Enterprise Library or any other 3rd-party library exists for their own abstraction purposes, not for yours.

What you need to do is define your own interfaces which describe the needs of your application (logging, caching, encryption, etc.) and then write adapters which implement your interfaces using Enterprise Library (or other 3rd-party libraries). This practice is known as the Dependency Inversion Principle.

To test your own code designed in this way, for unit/component level tests you would just use Test Doubles for those interfaces you've defined yourself (e.g. IMyOwnLogger). To test the adapters you write to adapt to 3rd-party libraries, you would write integration tests. To test that it all works together, you would write acceptance tests that drive the app through the UI or subcutaneously.

For more information on this view, check out my article: "TDD Best Practices: Don't Mock Others".

Derek Greer
  • 15,454
  • 5
  • 45
  • 52
  • I changed the title to reflect that fact that I don't want to test Enterprise Library but my own code that depends on Enterprise Library. – Bob Wintemberg Apr 02 '12 at 16:22
  • I've updated the answer to speak explicitly to how you would test your own code in light of external dependencies. – Derek Greer Apr 02 '12 at 16:37
  • Btw defining own interfaces which describe the needs of your application(e.g. logging) sounds good. Separating infrastructure from business code is great. But losing cool framework features, adding another abstraction level, losing optimizations and performance, is definitely not good. – Sergey Berezovskiy Apr 02 '12 at 21:02
  • You aren't losing framework features if your app doesn't need them. Following good design methodology, you should start by defining what your application needs and then adapt any external dependencies that fall out of that design to available frameworks. Proceeding in this fashion, you'll never end up with a feature you need but are somehow cut off from. I'm not suggesting that you write lowest common denominator interfaces, but rather that you write the interfaces that your app needs. As far as the Adaptor pattern being detrimental to you app in some way, I haven't seen that personally. – Derek Greer Apr 03 '12 at 15:59
  • @DerekGreer your article blog is now a spam site. For other you can see the original content thanks to the web archive project : https://web.archive.org/web/20140923101818/http://freshbrewedcode.com/derekgreer/2012/04/01/tdd-best-practices-dont-mock-others/ – bric3 Oct 02 '18 at 13:27
4

It is preferable to program against abstraction instead of implementation. But abstraction is not always interface. It could be abstract class.

public abstract class LogWriter
{
    public abstract void Write(string message);
}

So, there is no problem to create mock of abstract class:

Mock<LogWriter> logWriter = new Mock<LogWriter>();
TaxCalculator calc = new TaxCalculator(logWriter.Object);

If you not doing unit-testing, I don't see any problem to pass non-abstract parameters, because of YAGNI principle. If I don't need another implementation of ExceptionManager, then why should I create abstraction over it? But if I do TDD, then I definitely will need at least two implementations of class. One real and one mock/stub.

Btw be careful with service locator anti-pattern.

UPDATE: Didn't get that you are referring to existing classes of Microsoft.Practices.EnterpriseLibrary (which I don't like). I think thats another design failure of Microsoft.Practices team. Making 'sealed' ExceptionManager class which does not implement any interfaces/base classes kills testability.

Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • Abstractions in 3rd-party libraries are there for their own purposes, not to provide you with an abstraction to use. Also, Enterprise Library is probably the perfect scenario for using the sealed keyword. P&P has always said they don't intend EL to be used directly, but to be an example for writing code. They don't want people extending their stuff and then complaining when they make a design change in later versions. – Derek Greer Apr 02 '12 at 16:46
  • So, you think e.g. ILog in log4net is not to provide me with an abstraction to use? – Sergey Berezovskiy Apr 02 '12 at 20:13
  • Take that statement as hyperbole if you like. Different software vendors include interfaces for different reasons. Sometimes interfaces are present as a byproduct of the vendor's testing methodology. Other times there may be multiple implementations which a higher-level component relies upon. Some vendors may provide interfaces as a seam in hopes of allowing future design changes to vary without modifying the public interface. And yes, some may provide interfaces thinking it will be the one abstraction to rule them all. Regardless, you shouldn't rely on them within your own designs. – Derek Greer Apr 03 '12 at 15:50
  • 1
    ExceptionManager isn't sealed. In fact, it (and the other *manager classes in Entlib) are abstract classes, specifically to allow mocking. Just because they're not interfaces doesn't mean they aren't abstractions. – Chris Tavares Apr 04 '12 at 22:07
3

As long as your classes are not sealed, a competent mocking framework can create a subclass that acts exactly like a mocked interface implementation. There are more considerations to make when you're dependent on concrete classes - sealed methods will still execute on the specified class, etc. - but in general it's no different from depending on an interface.

Matt Mills
  • 8,692
  • 6
  • 40
  • 64