Unit Testing Singletons
The Singleton
I would say that I fall into the camp that generally shies away from the use of singletons. To start, singletons are a form of global state that is to be avoided. They carry state across the entire execution of the program making both testing and debugging difficult. It becomes hard to execute tests in isolation because they may modify the state of the singleton in ways that undesirably ties tests to a strict execution order. Beyond problems with global state, objects that use singletons are highly coupled to the both the way the singleton is instantiated and the way it is implemented. It isn’t possible to test an object that depends on a singleton without testing (or in someway relying on the behavior of) the singleton as well. Furthermore, this coupling is hidden from the user of the object. To correctly interact with the class depending on the singleton, a dependency that is not explicit in its interface, you must often also understand the behavior of the singleton. In other words, the state and behavior of the singleton can affect the behavior of the depending object as well as transitive side effects.
This post wasn’t meant to be an analysis of the singleton pattern so I won’t go into additional details. You can easily do a Google search for more information on this debate. But the background is provided to frame why you need to use some different techniques for testing singletons. The take away point should be that if you find yourself creating a singleton, take a step back and reconsider your design and only use the singleton as a last resort. Even if you really want only a single instance of a particular class, it is preferable for a dependency injection container or a factory object handle that requirement rather than the object itself.
Testing Singletons
Now that I’ve told you to avoid singletons if you can, I’m going to also say that you probably will at some point find a reason to implement one. Or it may be the case that you are maintaining some legacy code or third party libraries that use them. In that case, you are going to want to test them. To start the discussion, here is one of your standard singleton implementations in Java.
public class MySingleton{ private static MySingleton instance; private MySingleton(){ ... } public static synchronized MySingleton getInstance() { if (instance == null) { instance = new MySingleton(); } return instance; } ... }
The different ways of implementing singletons in Java have been discussed in great detail other places so I won’t discuss them here. This IBM developerWorks article (with the updated information that it links to) is a good resource if you would like more background.
One scenario you may need to test is the instantiation of you singleton. For example, your implementation may query some system properties to figure out how to initialize itself. You may also want to test the standard instance methods. Testing of neither of these scenarios is guaranteed to work consistently in a test case since the singleton carries its state throughout the life of the program. You can’t guarantee a consistent starting state from test to test. This is shown in the contrived example below.
@Test public void somethingIsDoneWithAbcIsSetAsASystemProperty(){ System.setProperty("com.example", "abc"); MySingleton singleton = MySingleton.getInstance(); assertThat(singleton.getSomething(), is("something")); } @Test public void somethingElseIsDoneWithXyzIsSetAsASystemProperty(){ System.setProperty("com.example", "xyz"); MySingleton singleton = MySingleton.getInstance(); // This will fail because the single instance was already initalized with abc in the // previous test assertThat(singleton.getSomething(), is("something else")); } @Test public void whenTheSingletonIsStartedAndPauseIsInvokedTheStateIsChanged(){ MySingleton singleton = MySingleton.getInstance(); singleton.pause(); assertThat(singleton.getState(), is("paused")); } @Test public void theNumberOfStateChangesIsTracked(){ MySingleton singleton = MySingleton.getInstance(); singleton.pause(); singleton.start(); singleton.stop(); // This will fail because the previous test invoked a state change assertThat(singleton.getNumberOfTimesStateChanged(), is(3)); }
By using reflection in one of your JUnit setup (i.e. @Before) methods, you can make sure each test starts with an uninitialized singleton and your tests are no longer affecting each other.
@Before public void resetSingleton() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { Field instance = MySingleton.class.getDeclaredField("instance"); instance.setAccessible(true); instance.set(null, null); }
Now, the instance field is reset to null before each test case executes guaranteeing consistent state.
Testing Objects That Depend on Singletons
Consider the Customer class below. When you write tests that should be testing the logic around how you calculate the discount, you need to keep all the dependencies in a consistent state so that you can test your logic in isolation. The use of the singleton prohibits this.
public class Customer{ public int caculateDiscount(){ MySingleton singleton = MySingleton.getInstance(); String something = singleton.getSomething(); // do some calculations based on the value of something return ...; } }
For testing objects that depend on singletons, you could also use the reflection approach above to better control the state of the singleton although it doesn’t help decouple your object under test from the singleton. The fact that you have to do this to test these objects clearly shows the coupling between the two objects. It should also be a bad testing smell that a Customer test has to fiddle with the internals of the MySingleton class when it isn’t even apparent through the interface that the MySingleton is a dependency.
public class CustomerTest { @Before public void resetMySingleton() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { Field instance = MySingleton.class.getDeclaredField("instance"); instance.setAccessible(true); instance.set(null, null); } @Test public void theDiscountShouldBe5PercentWhenSomethingOccurs(){ // Test logic specific to the Customer } }
Some Singleton Implementations Notes
To use the approach I’ve showed exactly the way I do above there are a couple very small requirements for the way you implement your singletons. They shouldn’t be a problem though because you probably should be implementing them these ways (for correctness and safety) anyways.
The design restriction is that the field that you store you single instance in within your Singleton must not be final. So the implementation shown above works fine.
And an alternative implementation where you instance isn’t lazily loaded is also fine if you make one small change to the reflection code (and you have a no-arg constructor).
public class MySingleton{ private static MySingleton instance = new MySingleton(); ... }
The reflection code in your test would be:
@After public void resetLog4jManagerSingleton() throws IllegalArgumentException, IllegalAccessException, InstantiationException, SecurityException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException { Field instance = MySingleton.class.getDeclaredField("instance"); instance.setAccessible(true); Constructor constructor = MySingleton.class.getDeclaredConstructor(); constructor.setAccessible(true); instance.set(null, constructor.newInstance()); }
But the following implementation won’t work and will result in a
java.lang.IllegalAccessException: Can not set static final com.example.MySingleton field com.example.MySingleton.instance to com.example.MySingleton
public class MySingleton{ private static final MySingleton instance = new MySingleton(); ... }
Basically, you can’t modify a static final field with reflection (although you can modify a non-static final field in some versions of the JDK. See http://www.javaspecialists.eu/archive/Issue096.html for an in depth analysis if you are interested in this reflection stuff).
There are some other approaches for lazy initialization of singletons with supposedly better performance. For example, the Initialization on Demand Holder (IODH) idiom. I haven’t really thought it through or tried it since I haven’t used the idiom, but I think the reflection code could be modified to support this approach.
December 12th, 2019 at 2:42 pm
Thank you, I value this. mytlolitang motilium retin Side Effecvts Citalopram Generic Gabapentin Dosage Augmentin Antibiotic viagra from canada london drugs canada
December 12th, 2019 at 3:00 pm
Excellent postings. Appreciate it! Can You Smoke Wellbutrin
December 12th, 2019 at 3:02 pm
Thanks a lot, I like this! Get Cialis Soft 20mg Without Prescription generic cymbalta Valium With Citalopram canadian pharmacies-24h
December 12th, 2019 at 3:05 pm
You actually reported that fantastically. Celebrex And Varicous Veins
December 12th, 2019 at 3:08 pm
Many thanks. I appreciate this! Doxycycline Sleep Over The Counter Cialis In Us Pharmacy
December 12th, 2019 at 3:10 pm
You actually expressed it exceptionally well! canadian viagra
December 12th, 2019 at 3:37 pm
Many thanks! Fantastic information. Quit Taking Allopurinol
December 12th, 2019 at 3:41 pm
Awesome postings. Cheers! https://canadianonlinepharmacytrust.com/
December 12th, 2019 at 3:44 pm
Nicely put. Thanks a lot! canadian drugs Taking Wellbutrin And Phentermine Together methocarbamol 750 mg valganciclovir Is Atarax The Same As Xanax
December 12th, 2019 at 3:47 pm
You made your stand extremely effectively.. https://viagrabestbuyrx.com/
December 12th, 2019 at 3:54 pm
With thanks, I appreciate this. https://rxdiflucan.com/
December 12th, 2019 at 4:02 pm
Point certainly considered!! [url=https://canadianpharmaciescubarx.com/]canadian pharmacy viagra brand[/url]
December 12th, 2019 at 4:03 pm
You explained that perfectly! viagra without a doctor’s prescription
December 12th, 2019 at 4:08 pm
Really lots of very good data. levitra
December 12th, 2019 at 4:14 pm
You actually expressed that terrifically! hydrochlorot doxycycline hyclate baclofen 10 mg pill
December 12th, 2019 at 4:24 pm
Many thanks! I appreciate it. [url=https://viaonlinebuymsn.com/]100mg viagra without a doctor prescription[/url]
December 12th, 2019 at 4:27 pm
Nicely put, With thanks! [url=https://buymodafinilntx.com/]modafinil dosage[/url]
December 12th, 2019 at 4:33 pm
You stated it superbly! https://celebrex-phermacy.com/
December 12th, 2019 at 4:35 pm
You actually suggested that superbly. [url=https://buymodafinilntx.com/]modafinil 200mg[/url]
December 12th, 2019 at 4:51 pm
Nicely put. Thanks. retin-a cream
December 12th, 2019 at 4:54 pm
Fine information. Thanks. Augmentin Et Viagra
December 12th, 2019 at 4:57 pm
Cheers, Useful information. duloxetine 60 mg
December 12th, 2019 at 5:00 pm
Well voiced really. ! hydrochlorothiazide 25 mg levitra generic Bactrim 800600mgno Rx Required Canada celecoxib 200 mg atarax
December 12th, 2019 at 5:04 pm
Thank you! Lots of stuff.
buy finasteride online
December 12th, 2019 at 5:06 pm
Thanks! Quite a lot of knowledge!
https://advairdiskus-247buy.com/
December 12th, 2019 at 5:10 pm
Thank you! I value it! lexapro medication
December 12th, 2019 at 5:15 pm
Thanks a lot! An abundance of stuff!
online kamagra
December 12th, 2019 at 5:16 pm
Amazing a lot of terrific knowledge! Citalopram Lifescript Zoloft And Lamictal Generic Doxycycline Hyclate 100mg Capsules acyclovir 400 mg
December 12th, 2019 at 5:24 pm
You said this well! kamagrakfg kamagra oral jelly
December 12th, 2019 at 5:26 pm
You said it very well.! https://cialistl.com/
December 12th, 2019 at 5:37 pm
Nicely put, Thanks! promethazine syrup advair hfa drugs for sale antibiotic amoxicillin cephalexin inderal xl Lawyer Celebrex Wisconsin
December 12th, 2019 at 6:07 pm
Really a lot of helpful information! furosemide
December 12th, 2019 at 6:11 pm
Seriously a lot of good facts! canadian online pharmacy
December 12th, 2019 at 6:14 pm
Thanks a lot! Plenty of tips!
Med Hydrochlorothiazide
December 12th, 2019 at 6:20 pm
Wow tons of awesome knowledge. acyclovir 400 mg
December 12th, 2019 at 6:23 pm
Cheers. Loads of information!
Acyclovir 400mg Information
December 12th, 2019 at 6:37 pm
Factor clearly used!! wellbutrin xl
December 12th, 2019 at 6:40 pm
With thanks. I like it. Promethazine 25mg What Does It Do
December 12th, 2019 at 6:51 pm
Valuable knowledge. Thanks a lot! health canada drug database ejaculation with flomax sertraline medication bactrim forte doxycycline 100 mg keflex
December 12th, 2019 at 7:01 pm
Thank you, Good stuff! Buy Amoxicillin Capsules Online tretinoin gel valaciclovir amoxicillin 500mg
December 12th, 2019 at 7:09 pm
You said it perfectly.! cephalexin antibiotic canadian online pharmacies Cheap Motilium 10mg Without Subscription Allopurinol 300 Mg For Sale canadian pharmaceuticals online
December 12th, 2019 at 7:11 pm
Amazing many of awesome advice! https://viaonlinebuyntx.com/
December 12th, 2019 at 7:17 pm
This is nicely expressed! . https://rxdiflucan.com/
December 12th, 2019 at 7:24 pm
You actually explained it superbly! https://discount-allopurinol.com/