Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initialize and clean up per-test data for parallel tests in TestNG

While migrating some tests from JUnit to TestNG, I'm facing an issue because of the difference in how these test frameworks treat their Test class instances.

JUnit creates a new instance of the Test class for each test method. So a common pattern I see is:

public class MyTest {

    private Stream inputData;

    @Before
    public void setUp() {
        // Set up some data in (non-static) instance fields
        // This data is isolated per test
        inputData = createInputDataStream();
    }

    @Test
    public void testStuff() {
        // Use the data from the instance fields
        doStuff(inputData);
    }

    @After
    public void tearDown() {
        // Clean up the data from the instance fields
        closeInputDataStream(inputData);
    }
}

In contrast, TestNG uses a single instance of the Test class for all test methods. So the pattern above does not work! Because data is stored in instance fields, the values are no longer isolated. This can cause overwritten data mid-test if parallel execution is enabled.

So how would I do this with TestNG? Is there a way to store data which is isolated to each @BeforeMethod-@Test-@AfterMethod tuple?

I can do all 3 steps inside the @Test itself, but that would require adding ungainly try...finally blocks to every test. I also tried using ITestContext, but it also seems to be shared for the entire test run.

like image 287
metacubed Avatar asked Nov 10 '22 05:11

metacubed


1 Answers

Yes, with TestNG you have way more power over those local variables that you did with JUnit AND the DataProvider handles your threading, per test-class-instance:

public class MyTest {

private Stream inputData;

@BeforeMethod
public void setUp(Object[] args) {
    inputData = (Data)args[0]; 
    inputData = normalizeDataBeforeTest(inputData);
}

@Test(dataProvider="testArgs")
public void testStuff(Data inputDatax, Object a, Object b) {
    doSomethingWith(a);
    doSomethingWith(b);
    doStuff(this.inputData);
}

@AfterMethod
public void tearDown() {
    // Clean up the data from the instance fields
    closeInputDataStream(inputData);
}

....
@DataProvider
public static Object[][] testArgs() {
    // generate data here, 1 row per test thread
  Object[][] testData;
  for loop {
      // add row of data to testData
      // {{dataItem, obja, objb}, {dataItem, obja, objb}} etc.
  }
  return testData;
}
}
like image 115
djangofan Avatar answered Nov 14 '22 23:11

djangofan