Reputation: 3252
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;
}
}
Upvotes: 5
Views: 2199
Reputation: 16262
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".
Upvotes: 9
Reputation: 236218
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.
Upvotes: 4
Reputation: 8792
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.
Upvotes: 3