Initialize and clean up per-test data for parallel

2019-04-29 12:43发布

问题:

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.

回答1:

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;
}
}