Let me explain it with an example.
Lets say we have a factory which creates arrows for archers in a city. Every time a archer comes it gives 2*n arrows to it where n is the number of the soldier while making sure no two archers get the arrows at the same time.
It comes naturally that this factory should be a singleton instance, since archers can come from all over the place. So we write a singleton class like this
public class WeaponFactory {
private int arrowCount = 0;
private WeaponFactory(){
// make it private so that no one can create it
}
//Let class loader do the magic of creating a singleton Instance for u
public static WeaponFactory getWamboo(){
return WeaponFactoryHolder.instance;
}
private static class WeaponFactoryHolder {
static final WeaponFactory instance = new WeaponFactory();
}
//no two archers can take an arrow at the same time
public synchronized Weapon[] getArrows() {
arrowCount++;
Weapon weapons[] = new Arrow[this.arrowCount];
for(int i = 0; i < arrowCount; i++){
weapons[i] = new Arrow(Arrow.FIRE_POWER);
}
return weapons;
}
//to create an arrow with specified firepower
public Weapon createArrow(int firePower){
return new Arrow(firePower);
}
}
Its a pretty standard class which uses singleton design pattern and fulfills the requirement. Now to test this class we have bunch of tests. For example lets say we have these two classes
public class WeaponFactoryTest1 extends TestCase {
public void testFactory(){
WeaponFactory weaponFactory = WeaponFactory.getWamboo();
Weapon[] weapons = weaponFactory.getArrows();
Assert.assertEquals(1, weapons.length);
}
}
public class WeaponFactoryTest2 extends TestCase {
public void testFactory(){
WeaponFactory weaponFactory = WeaponFactory.getWamboo();
Weapon[] weapons = weaponFactory.getArrows();
Assert.assertEquals(1, weapons.length);
}
}
Now as long as the two tests run in a different JVM we are fine, each test will have the newly created singleton instance of WeaponFactory and the test logic will test the classes correctly. But the moment both test run in the same JVM(e.g. mvn clean install command is fired, all tests will run in the same JVM) we have an issue. In the above example among the two tests the one which ran first will pass while the next one will fail since the first test created the instance of WeaponFactory and made changes to its state which the second test was not expecting.
public void reset(){
this.arrowCount = 0;
}
@Override
protected void setUp() throws Exception {
Marina.getInstance().reset();
WeaponFactory.getWamboo().reset();
EnemyFactory.getInstance().reset();
}
The point here is that its always tricky to test singleton classes in your code and it can be pretty confusing to figure out why a test which passes when its run individually fails when "mvn clean install" command is fired and while designing your singleton classes you should keep this in mind.
No comments:
Post a Comment