TestNG - sharing state between test classes - java

I have a testng suite of test classes that I am running via a testng.xml file. This works fine. All tests are run serially, so no parallel-execution hurdles.
My goal now is to take state that was generated by the test methods in one test class (e.g., a customerId primary key value generated by a database insert) and pass it along to another test class, so that the test methods in that second class can take further action based on that shared state (e.g., look up the just-inserted customer using the customerId value from the first class).
It's easy to share state between test methods in a single class, of course, via class member variables, but I don't see how to share it across test classes.

Really Testng-y way to do this is to set ITestContext attribute with desired value and then get it from other test class.
Set value:
#Test
public void setvaluetest(ITestContext context) {
String customerId = "GDFg34fDF";
context.setAttribute("customerId", customerId);
}
Get value:
#Test
public void getvaluetest(ITestContext context) {
String id = context.getAttribute("customerId");
}

Use a factory to create a repository. The repository stores "interesting test state" properties.
TestRepository tr = TestRepositoryFactory.getInstance();
...
tr.store("test13.PKID", pkid.toString());
and then in your subsequent code, repeat the call to the factory and then get the value:
String spkid = tr.get("test13.PKID");

Another way is to use means of object oriented programming.
Common structure of tests for example:
TestBase.java (parent class for all other test classes, has methods like #BeforeTest, #AfterSuite, etc.)
RegistrationTests.java (extends TestBase, )
ShoppingTests.java (extends TestBase)
and WhateverElseTests.java (extends TestBase)
So TestBase has all shared data as static fields e.g. Customer object:
public class TestBase {
protected static final BrowserManager bw = new BrowserManager();
protected static Customer customer;
#BeforeSuite
public void initBrowser() {
bw.init();
}
#AfterSuite
public void terminateBrowser() {
bw.terminate();
}
}
And access to customer in tests, e.g. ShoppingTests.java:
public class ShoppingTests extends TestBase {
#Test
public void doSomethingTest() {
bw.navigateTo().shoppingPage();
bw.shoppingPreparationHelper().checkDisplayedName(customer.name);
...
N.B.: tests that share objects should go in a strict sequence (first - test that init object, then - test that uses object's data), so use #Test(dependsOnMethods = "someMethodTest"). Otherwise you are in risk of NullPointerException for customer.
P.S.: Object-oriented way has great advantage over ITestContext because you can pass any object(s) from test to test (also between classes), not just string attribute.

As we know from TestNG JavaDoc, ITestContext defines a test context which contains all the information for a given test run. An instance of this context is passed to the test listeners so they can query information about their environment.
So we can share generated data from one class in this test with another class in this test.
Producer.java
public List<String> groupIds = ...;
#AfterClass(alwaysRun = true)
public void reserveGroupIds(ITestContext ctx) {
ctx.setAttribute(GROUPS_ATTR, groupIds);
}
Consumer.java
public List<String> groupIds;
#BeforeClass(alwaysRun = true)
public void fetchGroupIds(ITestContext ctx) {
groupIds = (List<String>) ctx.getAttribute(Producer.GROUPS_ATTR);
}
mySuite.xml
...
<test>
<classes>
<class name= "Producer"/>
<class name= "Consumer"/>
</classes>
</test>
...

Related

Running a #BeforeMethod testNG annotation when the method *contains* a group

If I have a beforeMethod with a group, and I run a different group, but within that group there exists a test that has both the group that I'm running as well as the group with the beforeMethod, I want that test to run its beforemethod. So for example:
#BeforeMethod(groups = "a")
public void setupForGroupA() {
...
}
#Test(groups = {"supplemental", "a"})
public void test() {
...
}
when I run testNG with groups=supplemental, I still want the beforeMethod to run before test, but because the group is supplemental instead of 'a', it won't.
This seems like such an obvious feature to me that I feel like I must be using the groups incorrectly, so I would also like to explain my workflow as well, in case that's where my issue is.
I'm using groups to define different layers of tests, as well as whether they need their own account to be created or if they need to use a proxy to access their data, etc. I'll have groups of smoke, supplemental and regression as well as groups of uniqueAccount, proxy, etc. I don't need specific setup for the first groupings, but those are the groups I pass in to run in maven. I require specific setups for the latter groups, but I never want to run just the tests that require a proxy, or require a unique account.
Groups configuration is not evaluated at runtime. test method won't activate setupForGroupA method.
The feature is used to find methods you want to run.
According to the following example:
#BeforeMethod(groups = "a")
public void setupForGroupA() {
...
}
#Test(groups = {"supplemental", "a"})
public void test() {
...
}
#Test(groups = {"supplemental"})
public void test2() {
...
}
If you run this class with group "a" it will run setupForGroupA and test methods because they are marked with the group "a".
If you run this class with group "supplemental" it will run test and test2 methods because they are marked with the group "supplemental".
It looks you have different behavior for some methods, so a good approach is to separate methods in different classes and select tests by class instead of select tests by groups.
public class A {
#BeforeMethod
public void setupForGroupA() {
...
}
#Test
public void test() {
...
}
}
and
public class Supplemental {
#Test
public void test2() {
...
}
}
Running class A will run setupForGroupA and test only.
Running class Supplemental will run test2 only.
Running both classes will run everything.
In case you want to run both classes and filter by something else, you can implement your own logic with a method interceptor:
#MyCustomAnnotation(tags = "a", "supplemental")
public class A {
...
}
#MyCustomAnnotation(tags = "supplemental")
public class Supplemental {
...
}
public class MyInterceptor implements IMethodInterceptor {
public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
// for each method, if its class contains the expected tag, then add it to the list
// expect tag can be passed by a system property or in a parameter from the suite file (available from ITestContext)
}
}
If I get it correct, you want to run your before method every time. In this case, you could set alwaysRun=true for your before method like this-
#BeforeMethod(alwaysRun = true, groups = "a")
public void setupForGroupA() {
...
}
This one of the solutions you want.

Pass parameters to JUnit test from the TestSuite class

I want to create 2 JUnit TestSuites. They both utilize the same test classes, but they should each use different parameters. For example, in test suite A, I want my data to be collected from file A and to be written to database A. In test suite B, I want my data to be collected from file B and to be written to databaseB.
The reason I use testSuites for this is because:
I can put all the specific parameters in the testsuite classes
I can reuse the testclasses
I can choose which testsuite to run. I do not want all tests to always run with all possible paramaters!
The problem is I cannot really pass the parameters. I understand the way the Parameterized class works with JUnit, but it does not allow point 3 in the list above. If I use the code below it will run my test class with both databse connections, which is not what I want to achieve.
#RunWith(value = Parameterized.class)
public class TestCheckData
{
private File file;
private DatabaseSource databaseSource;
public TestCheckData(File file, DatabaseSource databaseSource)
{
this.file = file;
this.databaseSource = databaseSource;
}
#Parameters
public static Iterable<Object[]> data1()
{
return Arrays.asList(new Object[][]
{
{ TestSuiteA.DATA_FILE_A, TestSuite1.DATABASE_A },
{ TestSuiteB.DATA_FILE_B, TestSuite1.DATABASE_B }
});
}
I already find some way of passing configurations in a spring context in this question, but I'm not using any special framework.
Well, this would be a little unconventional, but you could add a different Test class to the beginning of each suite run that would set the parameters you want to use for that test. So you'd have classes like:
public abstract class StaticParameters {
public static File dataFileToUse = null;
public static DatabaseSource databaseToUse = null;
}
public class Suite1Params extends StaticParameters {
#BeforeClass
public static void setParams() {
dataFileToUse = DATA_FILE_A;
databaseToUse = DATABASE_A;
}
}
public class Suite2Params extends StaticParameters {
#BeforeClass
public static void setParams() {
dataFileToUse = DATA_FILE_B;
databaseToUse = DATABASE_B;
}
}
Then you'd just make Suite1Params or Suite2Params the first in your suite list. You might have to add a fake #Test entry to the params classes, I'm not sure if the Suite runner requires that.
You could modify the tests so that they get the parameters from a config file. This way you would always only have 1 Suite.
The path of the config file can be looked up via a System property.
Then on the invocation of the test suite, you could pass in a different config file by changing the property using the -D option on the JVM.
So for example if you named the proprerty env.properties then your command would be:
%java -Denv.properties=prod.config runMyTests
or
%java -Denv.properties=dev.config runMyTests
etc

JUNIT : run setup only once for a large number of test classes

I have a class, which I use as a basis for my unit tests. In this class I initialize the whole environment for my tests, setting up database mappings, enter a number of database records across multiple tables, etc. That class has a method with a #BeforeClass annotation which does the initialization. Next thing, I extend that class with specific classes in which I have #Test methods.
My question is, since the before class is exactly the same for all these test classes, how can I ensure that they are run only once for all the tests.
One simple solution is that I could keep all the tests in one class. However, the number of tests is huge, also they are categorised based on functional heads. So they are located in different classes. However since they need the exact same setup, they inherit the #BeforeClass. As a result the whole setup is done at least once per test class, taking much more time in total than I would prefer.
I could, though, put them all in various subpackages under one package, hence if there is a way, how I can run set up once for all the tests within that package, it would be great.
With JUnit4 test suite you can do something like this :
#RunWith(Suite.class)
#Suite.SuiteClasses({ Test1IT.class, Test2IT.class })
public class IntegrationTestSuite
{
#BeforeClass
public static void setUp()
{
System.out.println("Runs before all tests in the annotation above.");
}
#AfterClass
public static void tearDown()
{
System.out.println("Runs after all tests in the annotation above.");
}
}
Then you run this class as you would run a normal test class and it will run all of your tests.
JUnit doesn't support this, you will have to use the standard Java work-arounds for singletons: Move the common setup code into a static code block and then call an empty method in this class:
static {
...init code here...
}
public static void init() {} // Empty method to trigger the execution of the block above
Make sure that all tests call init(), for example my putting it into a #BeforeClass method. Or put the static code block into a shared base class.
Alternatively, use a global variable:
private static boolean initialize = true;
public static void init() {
if(!initialize) return;
initialize = false;
...init code here...
}
Create one base class for all tests:
public class BaseTest {
static{
/*** init code here ***/
}
}
and every test should inherit from it:
public class SomeTest extends BaseTest {
}
You can make one BaseTest class with a #BeforeClass method, then have all the other tests inherit from it. This way, when each test object is constructed, #BeforeClass gets executed.
Also avoid executing it just once for all the test suite, since all the test cases should be independent. #BeforeClass should execute only once each test case, not test suite.
If you can tolerate adding spring-test to your project, or you are using it already, then a good approach is to use the technique described here: How to load DBUnit test data once per case with Spring Test
Not sure if anyone still is using JUnit and trying to fix it without using Spring Runner (aka no spring integration). TestNG has this feature. But here is a JUnit based solution.
Create a RunOnce per thread operation like so. This maintains a list of classes for which the operation has run.
public class RunOnceOperation {
private static final ThreadLocal t = new ThreadLocal();
public void run(Function f) {
if (t.get() == null) {
t.set(Arrays.asList(getClass()));
f.apply(0);
} else {
if (!((List) t.get()).contains(getClass())) {
((List) t.get()).add(getClass());
f.apply(0);
}
}
}
}
Back in your unit test
#Before
public beforeTest() {
operation.run(new Function<Integer, Void>() {
#Override
public Void apply(Integer t) {
checkBeanProperties();
return null;
}
});
}
private void checkBeanProperties() {
//I only want to check this once per class.
//Also my bean check needs instance of the class and can't be static.
}
My function interface is like this:
interface Function<I,O> {
O apply(I i);
}
When you use this way, you can perform operations once per class using ThreadLocal.

TestNG test inheritance and groups

We have DAO tests that should run against both the real DAO/database, and against a mock dao to verify that the mock dao behaves the same as the real dao. To this end, we have a structure like this:
public abstract class DAOTestBase
{
public void testSimple()
{
// dummy assertion
assertTrue(true, "Hello");
}
}
#Test(groups = "fast")
public class TestMockDAO extends DAOTestBase
{
// setUp/tearDown and helper methods for mock
}
#Test(groups = "slow")
public class TestDAO extends DAOTestBase
{
// setUp/tearDown and helper methods for real DB
}
Unfortunately this doesn't work - TestNG doesn't think that the testSimple method is a test and hence won't run it. So instead I tried to annotate the testSimple method (or the DAOTestBase class):
A #Test annotation without any groups will lead to the same effect - the test won't run for either fast nor slow groups.
A #Test annotation with groups fast and slow will lead to the opposite effect - both TestMockDAO and TestDAO will be run regardless of whether only fast or only slow tests should be run.
A #Test annotation with a different group, say common, plus added dependsOnGroups="common" annotations in both TestMockDAO and TestDAO will also not work unless common is included in the groups to run which leads again to case 2 above (both TestMockDAO and TestDAO are run).
In the end, what I'm looking for is a way to be able to define the group for the inherited tests in the sub class, but it seems as if the #Test annotation is only applied to test methods in that very same class, not also to inherited methods that don't have a #Test annotation. Is there any other way to achieve this (without overriding all methods in the sub classes) ?
I am currently working through a similar situation.
A way to make test cases run is to use something like:
#Test
public void someTest() {
TestNG testng = new TestNG();
testng.setTestClasses(new Class[] { SomeTests.class });
testng.run();
}
Reference: http://testng.org/doc/documentation-main.html#running-testng-programmatically
Unforunately I am currently unable to get it to report the test cases within SomeTests.
Have you tried simply adding a #Test annotation on top of DAOTestBase? Each subclass will override it with its own group and this should make the method in the base a test method.
I am using TestNG 6.14.3 version and I found a solution using priority annotation.
Example:
I have a base test class:
public class TestBase {
#Test(priority = 0)
public void testA() {
assertTrue(true, "testA");
}
}
And another extended test class:
public class Test2 extends TestBase {
#Test(priority = 1)
public void testB() {
assertTrue(true, "testB");
}
}
When I run Test2 test class, I obtain the following esult:
testA: true
testB: true
I solved it this way:
The methods in the base class are in "base" group, but need to check if the test have been initialized.
public abstract DaoTestBase {
private boolean initialized = false;
#Test(groups = "base")
public void testSimple() {
if (!initialized) { return; }
// dummy assertion
assertTrue(true, "Hello");
}
}
The test is initialized in the child, in the BeforeClass annotated method.
#BeforeClass
protected void initialize() {
super.initialized = true;
}
If you annotate the parent class instead of the methods, you must pass inheritGroups=false and the group, since it inherits also the group of the base class and it will not work.
Now, you must run TestNG to check groups base,fast or base,slow. Both tests will be executed, but the one not initialized will do nothing.
It is ugly, and I would not recommend it (it looks better to redefine the methods in child and call the according super method), but in my case I need priority in my test methods, and I want to avoid that repetition in each child class.

Passing output of one test method to another method testng

I have to write the following unit test cases in testng:
saveProductTest which would return productId if product details are saved successfully in DB.
modifyProductTest, it should use previously saved productId as a parameter.
I am taking the product details input(PrdouctName, ReleaseDate) for saveProductTest and modifyProductTest method from an XML file using testNg data providers.Since productId is generated in save method, I have to pass it to the modify method.
What is the best way to pass output of one test method to another method in testng.
With all due respect to simendsjo, the fact that all tests should be independent from each other is a dogmatic approach that has a lot of exceptions.
Back to the original question: 1) use dependent methods and 2) store the intermediate result in a field (TestNG doesn't recreate your instances from scratch, so that field will retain its value).
For example
private int mResult;
#Test
public void f1() {
mResult = ...
}
#Test(dependsOnMethods = "f1")
public void f2() {
// use mResult
}
With the ITestContext object. It's a object available globally at the Suite context and disponible via parameter in each #Test.
For example:
#Test
public void test1(ITestContext context, Method method) throws Exception {
// ...
context.setAttribute(Constantes.LISTA_PEDIDOS, listPaisPedidos);
// ...
}
#Test
public void test2(ITestContext context, Method method) throws Exception {
List<PaisPedido> listPaisPedido = (List<PaisPedido>)
context.getAttribute(Constantes.LISTA_PEDIDOS);
// ...
}
Each unit test should be independent of other tests so you more easily can see what fails. You can have a helper method saving the product and returning the id and call this from both tests.

Categories

Resources