'TestNG - Test fixture in the style of pytest

Writing Appium tests with TestNG. I'd like to pass the AppiumDriver to my test methods as an argument, so I don't have to remember to get it in every test. For example:

class FooTest {
    @BeforeTest
    public void initDriver(){
        AppiumDriver driver = ...
        // do some black magic to add the driver as an argument for all tests in this class
    }
    
    @Test
    public void testLogin(AppiumDriver driver){
        // example
        driver.findElementByAccessibilityId("username").sendKeys("foo");
        driver.findElementByAccessibilityId("password").sendKeys("bar");
        // etc...
    }
}
  • I can't use a TestNG parameter because they can only be strings.
  • I don't want to use a DataProvider for this because I want to use them for other stuff
  • I don't want to put it in the ITestContext, because then I have to remember to get it. (This is what I'm currently doing)
  • I can't put it as a class property, because I plan to do parallel="methods"
  • I don't want to put it in a class property as a ThreadLocal, because then I have to do .get() constantly.
  • I would prefer to not have to specify it in the @Test decorator for every single test.


Solution 1:[1]

Well if you rule out all available options, there is not much you can do...

I think the best option would be to use the ThreadLocal approach. If the only concern you have with ThreadLocal is that you need to call get() all the time, simply wrap this call in small function:

private final ThreadLocal<AppiumDriver> driver = new ThreadLocal<>();

@BeforeTest
void initDriver() {
  final AppiumDriver driver = ...;
  ...
  return this.driver.set(driver);
}

private AppiumDriver driver() {
  this.driver.get();
}

@Test
public void testLogin() {
  // example
  driver().findElementByAccessibilityId("username").sendKeys("foo");
  driver().findElementByAccessibilityId("password").sendKeys("bar");
  // etc...
}

Solution 2:[2]

If you can use Junit 5.0 then you can use @Parameterizedtest with @MethodSource.

private static AppiumDriver driver() {
  //get driver.
}

private static Stream<Arguments> getDriver(){
  return Stream.of(Arguments.of(driver())); // return driver obtained from `driver()` method.
}

// use driver from `getDriver()`. 
@ParameterizedTest
@MethodSource({"getDriver"})
void testLogin(AppiumDriver driver){
// use driver for your tests.
}

Suppose if some of your tests don't need this driver, you can still use normal @Test methods instead of using @ParameterizedTest. Also, you can have as many arguments in getDriver if your tests required so.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 dpr
Solution 2 Ashish Patil