//
you're reading...
TestNG

Sharing data across Tests in different TestClasses

So today I saw another interesting question posted on the TestNG-Users google forum. How does one go about sharing data across test methods which are spilt over different test classes.

Here’s two ways which I can think of in which this can be done.

Approach 1: Via the TestClass’s instance.

Here’s how you do it.

Here are two classes that I will be using to demonstrate how to get this done.

First we will need an interface so that we can use it to access the shared data [ Program to interface is a very widely used technique in the world of Java ]

Here’s how the interface looks like :


public interface DataGrabber {
    List<Integer> getSumValue();
}

Now lets take a look at the class which apart from doing some tests on the data, also saves the data so that it can be used by other test classes. I am going to call it as Generator


package organized.chaos.testng;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static org.testng.Assert.assertTrue;

import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class Generator implements DataGrabber {
    private List<Integer> sumValue;

    public List<Integer> getSumValue() {
        return Collections.unmodifiableList(sumValue);
    }

    public Generator() {
        sumValue = new ArrayList<Integer>();
    }

    @Test(groups = "parent", dataProvider = "generateNumbers")
    public void testNumbers(Integer a, Integer b) {
        assertTrue(a != 0);
        assertTrue(b != 0);
        sumValue.add(a + b);
    }

    @DataProvider
    public Object[][] generateNumbers() {
        return new Object[][] { { 1, 2 }, { 3, 4 } };
    }

}

If you pay close attention you would notice that this class implements our DataGrabber interface. This is being done so that it becomes easy for us to read data from the Generator class’s instance using the interface.

Now lets look at our second class which is going to feed on the data that is generated by our Generator class. I am going to call it Consumer

package organized.chaos.testng;

import java.util.List;

import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;

import org.testng.ITestContext;
import org.testng.ITestNGMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class Consumer {
    private List<Integer> allTheValues = null;

    @BeforeMethod(alwaysRun = true)
    public void dataGrabber(ITestContext ctx) {
        for (ITestNGMethod eachMethod : ctx.getAllTestMethods()) {
            //We are specifically looking for the method testNumbers() because we know that once we find this method,
            //the instance of the class to which this method belongs to will have our details. 
            if (eachMethod.getConstructorOrMethod().getName().equals("testNumbers")) {
                //First we query the method to give out its instance
                Object testClassObject = eachMethod.getInstance();
                //Now we check if the instance implements our interface. We do this because we will cast the instance into the 
                //interface type to access our data
                if (testClassObject instanceof DataGrabber) {
                    allTheValues = ((DataGrabber) testClassObject).getSumValue();
                    //Ok.. now that we found our data, lets break out of the loop. No point in continuing further.
                    break;
                }
            }
        }

    }

    @Test(dependsOnGroups = "parent", groups = "child")
    public void myCrazyTest() {
        assertNotNull(allTheValues);
        assertFalse(allTheValues.isEmpty());
        assertTrue(allTheValues.contains(new Integer(3)));
        assertTrue(allTheValues.contains(new Integer(7)));

    }
}

And here’s how the suite file looks like, which is going to show how it runs :


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite" parallel="false" verbose="2">
	<test name="Test">
		<groups>
			<run>
				<include name="parent"></include>
				<include name="child"></include>
			</run>
		</groups>
		<classes>
			<class name="organized.chaos.testng.Generator" />
			<class name="organized.chaos.testng.Consumer" />
		</classes>
	</test> 
</suite> 


Approach 2 Via the <test> ‘s attributes.

This time, we aren’t going to be using any interface, but we will bank on the fact that TestNG lets me add attributes to a <test>.

Here’s how the Generator class would look like [ Lets call it Generatorv2 ]


package organized.chaos.testng;

import static org.testng.Assert.assertTrue;

import java.util.ArrayList;
import java.util.List;

import org.testng.ITestContext;
import org.testng.annotations.AfterClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class Generatorv2 {
    public static final String MY_ATTRIBUTE = "sumOfValues";
    private List<Integer> sumValue;

    @AfterClass(alwaysRun = true)
    public void insertValueIntoAttribute(ITestContext ctx) {
        ctx.setAttribute(MY_ATTRIBUTE, sumValue);
    }

    public Generatorv2() {
        sumValue = new ArrayList<Integer>();
    }

    @Test(groups = "parent", dataProvider = "generateNumbers")
    public void testNumbers(Integer a, Integer b) {
        assertTrue(a != 0);
        assertTrue(b != 0);
        sumValue.add(a + b);
    }

    @DataProvider
    public Object[][] generateNumbers() {
        return new Object[][] { { 1, 2 }, { 3, 4 } };
    }

}

As you can see, we are adding attributes to the ITestContext. ITestContext is TestNG’s way of representing a <test> tag.

Now lets look at how the Consumer class is going to look like [ yep, you guessed it. I am going to be calling it as Consumerv2.. Gosh! either you must be good in mind reading or am getting way too predictable with my class names šŸ™‚ ]


package organized.chaos.testng;

import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;

import java.util.List;

import org.testng.ITestContext;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class Consumerv2 {
    private List<Integer> allTheValues = null;

    @BeforeClass(alwaysRun=true)
    @SuppressWarnings("unchecked")
    public void fetchData(ITestContext ctx) {
        allTheValues = (List<Integer>) ctx.getAttribute(Generatorv2.MY_ATTRIBUTE);
    }

    @Test(dependsOnGroups = "parent", groups = "child")
    public void myCrazyTest() {
        assertNotNull(allTheValues);
        assertFalse(allTheValues.isEmpty());
        assertTrue(allTheValues.contains(new Integer(3)));
        assertTrue(allTheValues.contains(new Integer(7)));

    }
}

Now lets take a look at how our suite file would look like :


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite" parallel="false" verbose="2">
	<test name="Test">
		<groups>
			<run>
				<include name="parent"></include>
				<include name="child"></include>
			</run>
		</groups>
		<classes>
			<class name="organized.chaos.testng.Generatorv2" />
			<class name="organized.chaos.testng.Consumerv2" />
		</classes>
	</test> 
</suite> 

As you can see, there wasn’t much of a change in our suite file except for our class name.

Just one thing you would notice in both the suites. We are running via groups and our Test classes leverage dependsOnGroups. In case you are wondering why did I do this, it was just to force TestNG to run my tests from the Generator class first and then my tests from Consumer. If my Test methods all resided in the same class, I could have used dependsOnMethods but since they are now scattered across different Test classes, dependsOnGroups is the only way to force “order” amidst “chaos” šŸ™‚

Hope this post would help you get started with “Sharing data” among test classes.

Share [this blog I mean :)] and enjoy !

Advertisements

Discussion

4 thoughts on “Sharing data across Tests in different TestClasses

  1. For those that are using TestNG in integration tests and want to make the configuration of the deployed environment available to multiple tests they may want to look at using Dependency Injection. I have an example on my Blog: http://biggerwrench.blogspot.com/2014/02/testng-using-guice-for-dependency.html

    Posted by jmochel` | February 13, 2014, 7:41 pm
  2. Hi,
    I am Unable to install TestNG in Eclipse hellios service release2 as I am getting error “Unable to connect to repository” everytime.
    I tried different solution from google but still getting the same error. I am setting up this on my system in Office.

    Anyone kindly help in this regards.

    Thanks in advance.

    I know this is a very simple process to setup testNG in eclipse and even I have done this on my home PC very easily.

    Thanks

    Posted by ajay | March 31, 2015, 6:45 pm
  3. Hi thanks , but what if if we want to pass value to other attribute in TESTNG XML not in same

    Posted by Anurag | August 9, 2017, 2:25 pm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: