A suite that passes sequentially can fail the moment you enable parallel execution. Elements disappear mid-tap, one scenario's sendKeys lands on another scenario's screen, and a driver that worked a second ago throws NullPointerException. Rerun the failures alone and they all pass.
The cause is a single design fact. cucumber-spring builds one Spring ApplicationContext for the entire run. Your DriverManager is a @Component, so it's a singleton โ one AppiumDriver, handed to every thread that asks. Your screen objects are singletons too, with their @AndroidFindBy / @iOSXCUITFindBy proxies bound to whatever driver existed when the first scenario built them.
Two threads, one Appium session, interleaved commands.
What the article covers:
- ๐งต Moving the driver into a ThreadLocal so each thread owns its own
- ๐ Replacing @PostConstruct creation with an explicit createDriver() call on the scenario's thread
- ๐ช Driver lifecycle in Cucumber @Before / @After hooks, and why they belong in a class of their own
- ๐ฏ @ScenarioScope on screen objects, so element proxies bind to the correct driver
- โฑ๏ธ Why AppiumFieldDecorator's 1-second default stops working once drivers are created per scenario
- ๐ฆ Why screen objects have to move to src/test/java
One detail worth knowing now: quitDriver() must call ThreadLocal.remove(), not just driver.quit(). The JUnit Platform reuses worker threads across scenarios. Quit without removing and the closed driver stays in that thread's slot โ the next scenario scheduled onto it inherits a dead session.
The full walkthrough has the refactored DriverManager, the hooks class, and the @ScenarioScope change, with the reasoning behind each.
๐ Read the full guide here: https://www.mobile-automation.io/mobile-parallel-testing-part-1/
Top comments (0)