简体   繁体   中英

TestNG run method once for all test suite

//deleted, see a refactoring at Update section.

I have this piece of code I wrote for an example. When @BeforeClass annotation presents it sets the value to be what I expect it to be and the inheriting TestsOther class prints it correctly. But it runs testInit method each time , once for SomeTest and once for OtherTest .

So the obvious thing I thought to do is to change it to @BeforeTest annotation, so it would evaluate that method only once, before all tests, but then my inheriting TestsOther class gets null .

Can someone explain to me why it gets a null and what is the right way to make testInit method run only once for all the suite.

Thanks.

Update: To make it more clear I have refactored the code a little.

    public class TestsInit {

    protected String value;

    @BeforeClass
    public void testInit(){
        System.out.println("Entered testInit in TestsInit class,to set the value.");
        value="Something";
    }

    @Test
    public void SomeTest(){
        boolean isTrue=true;
        System.out.println("Entered SomeTest in TestsInit class.");
        Assert.assertTrue(isTrue);
    }
}

public class TestsOther extends TestsInit {

    @Test(dependsOnMethods = {"SomeTest"})
    public void OtherTest(){
        System.out.println("Entered OtherTest test in TestsOther class with value: "+value);
    }
}

The output I get is:

Entered testInit in TestsInit class,to set the value.
Entered SomeTest in TestsInit class.
Entered testInit in TestsInit class,to set the value.
Entered SomeTest in TestsInit class.
Entered OtherTest test in TestsOther class with value: Something

I want to look like this:

Entered testInit in TestsInit class,to set the value.
Entered SomeTest in TestsInit class.
Entered OtherTest test in TestsOther class with value: Something

So it will run the testInit method only once, and run the SomeTest test once before the TestsOther test. I need to store the value at parent class so all children can use it. I need to run the testInit only once and the SomeTest test only once also.

Any suggestions?

Here is the Answer to your Question:

I have taken your own code as it is as follows:

TestsInit Class:

package Q45436872;

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

public class TestsInit 
{

    protected String value;

    @BeforeClass
    public void testInit(){
    System.out.println("Entered testInit in TestsInit class,to set the value.");
    value="Something";
    }

    @Test
    public void SomeTest(){
    boolean isTrue=true;
    System.out.println("Entered SomeTest in TestsInit class.");
    Assert.assertTrue(isTrue);
    }
}

TestsOther Class:

package Q45436872;

import org.testng.annotations.Test;

public class TestsOther  extends TestsInit
{

    @Test (dependsOnMethods = {"SomeTest"})
    public void OtherTest(){
    System.out.println("Entered OtherTest test in TestsOther class with value: "+value);
    }
}

testng.xml :

 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite name="Suite"> <test name="Test"> <classes> <class name="Q45436872.TestsInit"/> <class name="Q45436872.TestsOther"/> </classes> </test> <!-- Test --> </suite> <!-- Suite --> 

Now when I run this setup as a TestNG Suite I do get the following result:

Entered testInit in TestsInit class,to set the value.
Entered SomeTest in TestsInit class.
Entered testInit in TestsInit class,to set the value.
Entered SomeTest in TestsInit class.
Entered OtherTest test in TestsOther class with value: Something

===============================================
Suite
Total tests run: 3, Failures: 0, Skips: 0
===============================================

I think till here we are on the same page.

Requirement:

Now, your requirement seems to:

  1. Run the testInit method only once.
  2. Run the SomeTest test once before the TestsOther test

Solution:

As solution, I just commented out the mandatory execution of TestsInit Class. So my updated testng.xml was as follows:

 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite name="Suite"> <test name="Test"> <classes> <!-- <class name="Q45436872.TestsInit"/> --> <class name="Q45436872.TestsOther"/> </classes> </test> <!-- Test --> </suite> <!-- Suite --> 

So now I am only executing TestsOther Class. TestsOther Class extends TestsInit . So as testInit() is within @BeforeClass annotation this method gets executed first.

Next, SomeTest() under @Test annotation gets executed which was inline with your requirement.

Finally, OtherTest() method under @Test annotation from TestsOther Class gets executed (which was tagged as (dependsOnMethods = {"SomeTest"}) ), which is again inline with your requirement.

The output on my console is:

Entered testInit in TestsInit class,to set the value.
Entered SomeTest in TestsInit class.
Entered OtherTest test in TestsOther class with value: Something

This output exactly matches to your requirement.

Let me know if this Answers your Question.

Both classes have tests, so they are test classes and @BeforeClass will be run once by class. With @BeforeTest , you see null because you have 2 instances but the method is only called on 1 instance (the other will have null value).

If you want to have only one call of the method without null , you need to store and find the value from the ITestContext .

@BeforeTest
public void testInit(ITestContext context) {
    context.setAttribute("value", "Something");
}

@Test(dependsOnMethods = {"SomeTest"})
public void OtherTest() {
    System.out.println(context.getAttribute("value"));
}

Adding a sample that explains the static variable usage

public class TestsInit {
    protected String value;
    private static boolean initialised = false;

    @BeforeClass
    public void testInit() {
        synchronized (TestsInit.class) {
            if (initialised) {
                return;
            }
            System.out.println("Entered testInit in TestsInit class,to set the value.");
            value = "Something";
            initialised = true;
        }
    }

    @Test
    public void SomeTest() {
        boolean isTrue = true;
        System.out.println("Entered SomeTest in TestsInit class.");
        Assert.assertTrue(isTrue);
    }
}

As @BeforeClass runs once by class and there are two classes in your scenario so it will run twice.

Output with @BeforeClass Annotation

Entered testInit in TestsInit class,to set the value.
Entered SomeTest in TestsInit class.
Entered OtherTest test in TestsOther class with value: Something
Entered testInit in TestsInit class,to set the value.
Entered SomeTest in TestsInit class.
PASSED: SomeTest
PASSED: OtherTest
PASSED: SomeTest

===============================================
    Default test
    Tests run: 3, Failures: 0, Skips: 0
===============================================


===============================================
Default suite
Total tests run: 3, Failures: 0, Skips: 0
===============================================

If you want to run TestsInit only once, then replace @BeforeClass to @BeforeSuite

Output with @BeforeSuite Annotation

Entered testInit in TestsInit class,to set the value.
Entered SomeTest in TestsInit class.
Entered OtherTest test in TestsOther class with value: Something
Entered SomeTest in TestsInit class.
PASSED: SomeTest
PASSED: OtherTest
PASSED: SomeTest

===============================================
    Default test
    Tests run: 3, Failures: 0, Skips: 0
===============================================


===============================================
Default suite
Total tests run: 3, Failures: 0, Skips: 0
===============================================

Notice one thing that SomeTest is running twice, it is because TestsOther is a different class which is extending TestsInit class and it has only one method which has a dependency on SomeTest testcase.

Hope this will help.

To execute testInit method only once for all classes , try to do initialization part in separate abstract class and extends other classes with this class where required.

If you are receiving output like this

Something

PASSED: SomeTest

PASSED: OtherTest

According to understanding, @Test of parent class will run before @Test of current child class. since you have @Test in both classes, so in each class @Test is executed and print on console.

With @BeforeTest annotation, it is working fine as well.(this means i did'nt get null on this change)

Can you please share some more code if you are still getting null after this proposed solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM