Łukasz Makuch

Łukasz Makuch

Singletons are often misused but easy to test

A discussion on singletons:

Why are singletons so nice?

Because they are easy to use.

Why are they easy to use?

Because they provide us a way of getting an object we need.

Why are singletons bad?

Because they make testing hard.

Why do they make testing hard?

Because it's impossible to use their test doubles while testing code which depends on them.

Judging from my experience, none of those answers is correct.

Let's start from testing.

public class FloristShop {
  public void placeOrder() {
    DeliveryGuy.getInstance().deliver(flowers, address);

It's not so rare to hear that now every time the test suite runs, some poor guy delivers some flowers... somewhere. It happens in real life, because it's a real delivery service and not a test double, thus we're charged for that! Sounds awful! Fortunately that's not true. We don't even need a special mocking framework in order to use a test double of the DeliveryGuy. If our singleton has the following method:

public IDeliveryGuy getInstance()

there may be one like this as well:

public void setInstance(IDeliveryGuy instance)

so in the setup method of the test class we can simply write:

DeliveryGuy.setInstance(new DeliveryGuyTestDouble());

In the same way the main method of our application could set the real delivery service instance, so running the test suite without passing a fake IDeliveryGuy implementation to the setInstance method will cause an exception and won't accidentally send the real delivery guy to the address used in the test suite.

But what about the ease of use? Don't singletons make it easier to write the code? Sometimes they do, but only in languages without global variables, as they are nothing but global variables. Otherwise a global variable can be used, what's even easier. The main source of problems with singletons I've seen is using them for a totally different purpose than they are created for. Their main job isn't to address the question "How do I get this object?" but to limit the number of instances of some class to exactly one. In other words, making writing any code easier isn't what are they created for. It's making writing some code impossible. And they do it well. Unconsciously putting this restriction on our code often leads to a situation when we would like to use composition, but we can't. Take a look the following example which doesn't use the Singleton design pattern:

FloristShop mainShop = new FloristShop(new RegularDeliveryGuy());

Let's imagine we want to add support of a premium florist shop, which delivers orders faster. We can easily avoid duplication and changes of the already existing code by constructing the premium shop this way:

FloristShop mainShop = new FloristShop(
    new RegularDeliveryGuy()
FloristShop premiumShop = new FloristShop(
    new MotorDrivingDeliveryGuy()

As we can see, it would be impossible with singletons, because there could be just one delivery guy.

From the author of this post

  • howlong.app - a timesheet built for freelancers, not against them!