How do I isolate a singleton in my unit tests?
I have a static class in my program. In the static constructor I create instance of se开发者_StackOverflow中文版rvice. The proxy of service is a singleton.
I must write unit tests for this class, and certainly I want isolate this service. How can I do it?
In my project we use Rhino.Mocks.
Singletons are problematic from a unit testing point of view. I assume the classes using your Singleton are retrieving the instance via a static property:
class MyClass()
{
public MyClass()
{
var myService = StaticFoo.MyServiceInstance;
}
}
This is equivalent to creating a concrete class instance locally since the knowledge of "how to get to" your Singleton is part of your class implementation. What you will have to do is remove that knowledge and inject this dependency much like you would inject other dependencies on non-singletons. The most straight-forward way would be constructor injection:
class MyClass()
{
public MyClass(IService myService)
{
//..
}
}
Another needed part is either making sure all interface methods/properties in your service are defined as virtual or defining an interface or abstract base class that your service implements and which defines all the operations your class MyClass
is using on this service. This is needed since most unit testing frameworks (including Rhino-Mocks) can only mock virtual methods / properties.
An alternative I have seen but personally don't like much, is introducing a setter in your Singleton so you can "swap out" the concrete class with your mock object needed for unit testing. This setter would only be used for unit testing and can be marked as internal so classes outside your assembly don't have access. You can then use the InternalsVisibleTo
attribute to make a special exception for your unit testing assembly so it can see and use the setter.
The main advantage of this approach is that it requires less refactoring to arrive at a testable solution, but at a cost of "polluting" the interface of your static Singleton holder. Since in most circumstances Singletons are not needed in the first place, I would consider refactoring to constructor injection first.
You need to have a public initialization method on your class that accepts an instance of the service. You can then inject a mock object using the initialization method.
精彩评论