kal
kal

Reputation: 29351

Different Singleton instances with JUnit tests

I have a standalone singleton which successfully passes the test. But with a group of tests this fails since once a singleton is defined it does not allow to reset the instance.

Any ideas about how to go about this?

Upvotes: 27

Views: 23556

Answers (8)

Vargan
Vargan

Reputation: 1317

Very late to the party here, but for anyone looking for an answer, in case you don't want / cannot modify the code.

@BeforeEach
public void setup() {
    
    object = Singleton.getInstance();
}

@AfterEach
public void after() {
    // cleaning the singleton instance
    ReflectionTestUtils.setField(object , "internal_object_name", null);
}

your Singleton class should be something like this:

public final class Singleton {

private static Singleton internal_object_name;

private Singleton (){}
public static Singleton getInstance() {
    if (object == null)
         return new Singleton();
    else
         return internal_object_name;
 }

Upvotes: -1

Omar Al Kababji
Omar Al Kababji

Reputation: 1828

You can add a method to destroy the singleton, for example destroyMe(); where you deinitialize everything and set the instance of the singleton to null.

  public void destroyMe(){
   this.instance = null;
   //-- other stuff to turn it off.
}

I will leave synchronization problems though ;)

But why do you need to re-initialize your singleton for each test? It should not differ based on the concept of the singleton.

Upvotes: 3

Andy Dufresne
Andy Dufresne

Reputation: 6180

Spring provides the DirtiesContext annotation for this particular use case where you need new instances of the singleton beans for each testcase. It basically creates a new application context for each testcase/testclass which has this annotation applied.

Upvotes: 3

Michael Lloyd Lee mlk
Michael Lloyd Lee mlk

Reputation: 14661

I highly recommend moving away from Singletons as a design pattern, and using Singleton as a scope (Dependency Injection). This would simply make your problem go away.

But assuming you are stuck in the world of Singletons, then you have a few options depending on if you are testing the Singleton or the dependency.

If you are testing the dependant item then you can mock the Singleton using PowerMock and JMockIt. See my previous post about mocking Runtime.getRuntime for instructions on how to go about this.

If you are testing the Singleton then you need to relax the rules on construction, or give the Singleton a "Reset" method.

Upvotes: 2

orip
orip

Reputation: 75427

Don't use a singleton.

Specifically, the only difference between a singleton and a global variable is that the singleton tries to enforce a single instance (by making the constructor private, for example).

Instead, make the constructor public and write tests using new instances. In your actual program, use getInstance() to get the canonical global instance (or use an IOC container).

And remember that singletons are pathological liars.

If you're still too comfortable with the idea of a Singleton, instead of making the constructor public you can add a public (and static) factory method to create instances in a way that can't be used by accident, e.g.:

public static MyClass TEST_CreateInstance() {
  return new MyClass();
}

Upvotes: 15

topchef
topchef

Reputation: 19783

Singleton instance needs to be passed to SUT by test itself - that way you create singleton (and destroy) for each test. Adopting IoC and mocking framework, like Mockito, would render this approach almost trivial.

Upvotes: 0

manuel aldana
manuel aldana

Reputation: 16438

generally beware of singletons, most often they are evil, bad design and tend to represent big yucky global variables (which is bad for maintenance).

still to get tests in place first you can do:


static setInstance(...){ //package visibility or in difficult cases you have to use public
  instance = ...;
}

as said this is more a workaround. so get first tests place, but then refactor away from singleton pattern.

Upvotes: 1

Csaba_H
Csaba_H

Reputation: 8245

I assume you have a private static field within your singleton class to store the initialized instance.

If you do not want to modify your code, you can define a teardown method which run after every test, and in this method you set this static field to null via reflection as seen here.

Upvotes: 20

Related Questions