I recently read that making a class singleton makes it impossible to mock the objects of the class, which makes it difficult to test its clients. I could not immediately understand the underlying reason. Can someone please explain what makes it impossible to mock a singleton class? Also, are there any more problems associated with making a class singleton?
Of course, I could write something like don't use singleton, they are evil, use Guice/Spring/whatever but first, this wouldn't answer your question and second, you sometimes have to deal with singleton, when using legacy code for example.
So, let's not discuss the good or bad about singleton (there is another question for this) but let's see how to handle them during testing. First, let's look at a common implementation of the singleton:
public class Singleton {
private Singleton() { }
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
public String getFoo() {
return "bar";
}
}
There are two testing problems here:
The constructor is private so we can't extend it (and we can't control the creation of instances in tests but, well, that's the point of singletons).
The
getInstance
is static so it's hard to inject a fake instead of the singleton object in the code using the singleton.
For mocking frameworks based on inheritance and polymorphism, both points are obviously big issues. If you have the control of the code, one option is to make your singleton "more testable" by adding a setter allowing to tweak the internal field as described in Learn to Stop Worrying and Love the Singleton (you don't even need a mocking framework in that case). If you don't, modern mocking frameworks based on interception and AOP concepts allow to overcome the previously mentioned problems.
For example, Mocking Static Method Calls shows how to mock a Singleton using JMockit Expectations.
Another option would be to use PowerMock, an extension to Mockito
or JMock
which allows to mock stuff normally not mock-able like static, final, private or constructor methods. Also you can access the internals of a class.
synchronized
. –
Fulguration A Singleton, by definition, has exactly one instance. Hence its creation is strictly controlled by the class itself. Typically it is a concrete class, not an interface, and due to its private constructor it is not subclassable. Moreover, it is found actively by its clients (by calling Singleton.getInstance()
or an equivalent), so you can't easily use e.g. Dependency Injection to replace its "real" instance with a mock instance:
class Singleton {
private static final myInstance = new Singleton();
public static Singleton getInstance () { return myInstance; }
private Singleton() { ... }
// public methods
}
class Client {
public doSomething() {
Singleton singleton = Singleton.getInstance();
// use the singleton
}
}
For mocks, you would ideally need an interface which can be freely subclassed, and whose concrete implementation is provided to its client(s) by dependency injection.
You can relax the Singleton implementation to make it testable by
- providing an interface which can be implemented by a mock subclass as well as the "real" one
- adding a
setInstance
method to allow replacing the instance in unit tests
Example:
interface Singleton {
private static final myInstance;
public static Singleton getInstance() { return myInstance; }
public static void setInstance(Singleton newInstance) { myInstance = newInstance; }
// public method declarations
}
// Used in production
class RealSingleton implements Singleton {
// public methods
}
// Used in unit tests
class FakeSingleton implements Singleton {
// public methods
}
class ClientTest {
private Singleton testSingleton = new FakeSingleton();
@Test
public void test() {
Singleton.setSingleton(testSingleton);
client.doSomething();
// ...
}
}
As you see, you can only make your Singleton-using code unit testable by compromising the "cleanness" of the Singleton. In the end, it is best not to use it at all if you can avoid it.
Update: And here is the obligatory reference to Working Effectively With Legacy Code by Michael Feathers.
Singleton
, inside setInstance()
methods we can set the final variable myInstance
to something? –
Timothy private static final Singleton myInstance;
Error: Variable myInstance
might not have been initialized. And again as pointed by @Shobhit how can we set a final variable to something? –
Profluent The best way to mock a singleton is not to use them at all, or at least not in the traditional sense. A few practices you might want to look up are:
- programming to interfaces
- dependency injection
- inversion of control
So rather than having a single you access like this:
Singleton.getInstance().doSometing();
... define your "singleton" as an interface and have something else manage it's lifecycle and inject it where you need it, for instance as a private instance variable:
@Inject private Singleton mySingleton;
Then when you are unit testing the class/components/etc which depend on the singleton you can easily inject a mock version of it.
Most dependency injection containers will let you mark up a component as 'singleton', but it's up to the container to manage that.
Using the above practices makes it much easier to unit test your code and lets you focus on your functional logic instead of wiring logic. It also means your code really starts to become truly Object Oriented, as any use of static methods (including constructors) is debatably procedural. Thus your components start to also become truly reusable.
Check out Google Guice as a starter for 10:
http://code.google.com/p/google-guice/
You could also look at Spring and/or OSGi which can do this kind of thing. There's plenty of IOC / DI stuff out there. :)
It very much depends on the singleton implementation. But it mostly because it has a private constructor and hence you can't extend it. But you have the following option
- make an interface -
SingletonInterface
- make your singleton class implement that interface
- let
Singleton.getInstance()
returnSingletonInterface
- provide a mock implementation of
SingletonInterface
in your tests - set it in the
private static
field onSingleton
using reflection.
But you'd better avoid singletons (which represent a global state). This lecture explains some important design concepts from testability point of view.
It's not that the Singleton pattern is itself pure evil, but that is massively overused even in situations where it is inapproriate. Many developers think "Oh, I'll probably only ever need one of these so let's make it a singleton". In fact you should be thinking "I'll probably only ever need one of these, so let's construct one at the start of my program and pass references where it is needed."
The first problem with singleton and testing is not so much because of the singleton but due to laziness. Because of the convenience of getting a singleton, the dependency on the singleton object is often embedded directly into the methods which makes it very difficult to change the singleton to another object with the same interface but with a different implementation (for example, a mock object).
Instead of:
void foo() {
Bar bar = Bar.getInstance();
// etc...
}
prefer:
void foo(IBar bar) {
// etc...
}
Now you can test function foo
with a mocked bar
object which you can control. You've removed the dependency so that you can test foo
without testing bar
.
The other problem with singletons and testing is when testing the singleton itself. A singleton is (by design) very difficult to reconstruct, so for example you can only test the singleton contructor once. It's also possible that the single instance of Bar
retains state between tests, causing success or failure depending on the order that the tests are run.
There is a way to mock Singleton. Use powermock to mock static method and use Whitebox to invoke constructor YourClass mockHelper = Whitebox
.invokeConstructor(YourClass.class);
Whitebox.setInternalState(mockHelper, "yourdata",mockedData);
PowerMockito.mockStatic(YourClass.class);
Mockito.when(YourClass.getInstance()).thenReturn(mockHelper);
What is happening is that the Singleton byte code is changing in run-time .
enjoy
© 2022 - 2024 — McMap. All rights reserved.