How To Use Singleton Class to Manage Instance Variables in Automation Framework – Java

In the previous post, we have seen how maximum beginners and experienced Selenium professionals apply inheritance with TestNG annotations while setup test scripts and end up with encountering NullPointerException. We have solved NullPointerException by converting instance variables to Static variables.

We can solve the above problem using a Singleton design pattern as well. Let’s learn about it.

TestNG version used as below:-

  org.testng testng 7.1.0 test

Let’s think of a scenario. A money bank may have many branches and many registered customers can withdraw or deposit or access there account at any time any place. Bank has a centralized common server where details of each customer are stored. If a new branch is opened it will also access data from the same server. If a brach is closed then also server will have customer’s data. In short, there is a common place where the state of data is stored.

We can consider an example of money withdrawal from ATM. If a customer tries to withdraw money simultaneously from two places, surely it will fail in one place because they also have a common place where data is kept updated. We can call that common place as a Singleton class.

When we develop a class in such a way that it can have the only instance at any time, is called a Singleton class. If a class is not instantiated then instantiate it and if the class is already instantiated return the same instantiated instance of the class. If we allow only one instance of any class that means we will have a shared object (somewhat how does a class variable or static variable works). This will solve our problem explained in the previous post. Variables initialized in TestNG annotated method will be stored in a common object for usage wherever we want.

package SecondTestNullPointerExceptionWithSingleton;

public class SharedVariables {

        // Private instance of class
        private static final SharedVariables instance = new SharedVariables();

        // Private constructor to control object creation of class
        private SharedVariables() {
        }

        // Return unidealized instance of class 
        public static SharedVariables getInstance() {
                return instance;
        }

        // A private variable with getter and setter methods
        private String someVariable;

        public String getSomeVariable() {
                return someVariable;
        }

        public void setSomeVariable(String someVariable) {
                this.someVariable = someVariable;
        }
}
package SecondTestNullPointerExceptionWithSingleton;

import org.testng.annotations.BeforeSuite;

public class Setup {

        // Get same instance of SharedVariables always
        SharedVariables sharedVariables = SharedVariables.getInstance();

        @BeforeSuite
        public void setupVariable() {
                System.out.println("Executing setupVariable...");
                sharedVariables.setSomeVariable("someValue");
        }

}
package SecondTestNullPointerExceptionWithSingleton;

import org.testng.annotations.Test;

public class FirstTestNGClass extends Setup{
        
        @Test
        public void firstMethodOfFirstTestNGClass()
        {
                System.out.println("Executing firstMethodOfFirstTestNGClass...");
                System.out.println("Value of someVariable is : "+ sharedVariables.getSomeVariable());
        }
        
        @Test
        public void secondMethodOfFirstTestNGClass()
        {
                System.out.println("Executing secondMethodOfFirstTestNGClass...");
                System.out.println("Value of someVariable is : "+ sharedVariables.getSomeVariable());
        }
}
package SecondTestNullPointerExceptionWithSingleton;

import org.testng.annotations.Test;

public class SecondTestNGClass extends Setup{
        
        @Test
        public void firstMethodOfSecondTestNGClass()
        {
                System.out.println("Executing firstMethodOfSecondTestNGClass...");
                System.out.println("Value of someVariable is : "+ sharedVariables.getSomeVariable());
        }
        
        @Test
        public void secondMethodOfSecondTestNGClass()
        {
                System.out.println("Executing secondMethodOfSecondTestNGClass...");
                System.out.println("Value of someVariable is : "+ sharedVariables.getSomeVariable());
        }
}


        

[RemoteTestNG] detected TestNG version 7.0.1
Executing setupVariable...
Executing firstMethodOfFirstTestNGClass...
Value of someVariable is : someValue
Executing secondMethodOfFirstTestNGClass...
Value of someVariable is : someValue
Executing firstMethodOfSecondTestNGClass...
Value of someVariable is : someValue
Executing secondMethodOfSecondTestNGClass...
Value of someVariable is : someValue

===============================================
Suite
Total tests run: 4, Passes: 4, Failures: 0, Skips: 0
===============================================

You may ask in the above Singleton class creating variables in advance will be difficult and how can we save random variables. We can make out singleton class more advance with Map where will give user flexibility to save any data. This implementation even helps in sharing context among tests.

package SecondTestNullPointerExceptionWithSingleton; import java.util.HashMap; public class SharedVariablesMap { // Private instance of class private static final SharedVariablesMap instance = new SharedVariablesMap(); private static HashMap dataStore = new HashMap(); // Private constructor to control object creation of class private SharedVariablesMap() { } // Return unidealized instance of class public static SharedVariablesMap getInstance() { return instance; } public void store(String key, String value) { dataStore.put(key, value); } public Object get(String key) { return dataStore.get(key); }
}
package SecondTestNullPointerExceptionWithSingleton;

import org.testng.annotations.BeforeSuite;

public class Setup {

        // Get same instance of SharedVariables always
        SharedVariables sharedVariables = SharedVariables.getInstance();
        SharedVariablesMap sharedVariablesMap = SharedVariablesMap.getInstance();

        @BeforeSuite
        public void setupVariable() {
                System.out.println("Executing setupVariable...");
                sharedVariables.setSomeVariable("someValue");
                sharedVariablesMap.store("Name", "Amod");
        }

}
package SecondTestNullPointerExceptionWithSingleton;

import org.testng.annotations.Test;

public class FirstTestNGClass extends Setup{
        
        @Test
        public void firstMethodOfFirstTestNGClass()
        {
                System.out.println("Executing firstMethodOfFirstTestNGClass...");
                System.out.println("Value of someVariable is : "+ sharedVariables.getSomeVariable());
                System.out.println("Value of name is :"+ sharedVariablesMap.get("Name"));
        }
        
        @Test
        public void secondMethodOfFirstTestNGClass()
        {
                System.out.println("Executing secondMethodOfFirstTestNGClass...");
                System.out.println("Value of someVariable is : "+ sharedVariables.getSomeVariable());
                System.out.println("Value of name is :"+ sharedVariablesMap.get("Name"));
        }
}
package SecondTestNullPointerExceptionWithSingleton;

import org.testng.annotations.Test;

public class SecondTestNGClass extends Setup{
        
        @Test
        public void firstMethodOfSecondTestNGClass()
        {
                System.out.println("Executing firstMethodOfSecondTestNGClass...");
                System.out.println("Value of someVariable is : "+ sharedVariables.getSomeVariable());
                System.out.println("Value of name is :"+ sharedVariablesMap.get("Name"));
        }
        
        @Test
        public void secondMethodOfSecondTestNGClass()
        {
                System.out.println("Executing secondMethodOfSecondTestNGClass...");
                System.out.println("Value of someVariable is : "+ sharedVariables.getSomeVariable());
                System.out.println("Value of name is :"+ sharedVariablesMap.get("Name"));
        }
}

There is no change in TestNG xml.

[RemoteTestNG] detected TestNG version 7.0.1
Executing setupVariable...
Executing firstMethodOfFirstTestNGClass...
Value of someVariable is : someValue
Value of name is :Amod
Executing secondMethodOfFirstTestNGClass...
Value of someVariable is : someValue
Value of name is :Amod
Executing firstMethodOfSecondTestNGClass...
Value of someVariable is : someValue
Value of name is :Amod
Executing secondMethodOfSecondTestNGClass...
Value of someVariable is : someValue
Value of name is :Amod

===============================================
Suite
Total tests run: 4, Passes: 4, Failures: 0, Skips: 0
===============================================

You can download/clone the above sample project from here.

If you have any doubt, feel free to comment below.If you like my posts, please like, comment, share and subscribe.#ThanksForReading

#HappyLearning