如何解决使用 Cucumber-junit-platform-engine 和 Selenium WebDriver 运行测试会打开太多线程
我尝试将现有的 Maven 项目配置为使用 Cucumber-junit-platform-engine 运行。
我使用了 this 存储库作为灵感。
我添加了所需的 Maven 依赖项,如使用 spring-boot-starter-parent 2.4.5 版和 Cucumber-jvm 6.10.4 版的链接项目。
我将 junit-platform 属性设置如下:
cucumber.execution.parallel.enabled=true
cucumber.execution.parallel.config.strategy=fixed
cucumber.execution.parallel.config.fixed.parallelism=4
在 runner 类中使用了注释 @Cucumber
,在具有步骤定义的类中使用了 @SpringBootTest
。
创建并行线程似乎工作正常,但问题是它在开始时创建所有线程并打开与场景数量一样多的浏览器窗口(驱动程序)(例如 51 而不是 4)。
我正在使用 CucumberHooks
类在场景前后添加逻辑,我猜它会因为我使用的注释而干扰跑步者:
import java.util.List;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.Scenario;
import io.cucumber.plugin.ConcurrentEventListener;
import io.cucumber.plugin.event.EventHandler;
import io.cucumber.plugin.event.EventPublisher;
import io.cucumber.plugin.event.TestRunFinished;
import io.cucumber.plugin.event.TestRunStarted;
import io.github.bonigarcia.wdm.WebDriverManager;
public class CucumberHooks implements ConcurrentEventListener {
@Autowired
private ScenarioContext scenarioContext;
@Before
public void beforeScenario(Scenario scenario) {
scenarioContext.getNewDriverInstance();
scenarioContext.setScenario(scenario);
LOGGER.info("Driver initialized for scenario - {}",scenario.getName());
....
<some business logic here>
....
}
@After
public void afterScenario() {
Scenario scenario = scenarioContext.getScenario();
WebDriver driver = scenarioContext.getDriver();
takeErrorScreenshot(scenario,driver);
LOGGER.info("Driver will close for scenario - {}",scenario.getName());
driver.quit();
}
private void takeErrorScreenshot(Scenario scenario,WebDriver driver) {
if (scenario.isFailed()) {
final byte[] screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
scenario.attach(screenshot,"image/png","Failure");
}
}
@Override
public void setEventPublisher(EventPublisher eventPublisher) {
eventPublisher.registerHandlerFor(TestRunStarted.class,beforeAll);
}
private EventHandler<TestRunStarted> beforeAll = event -> {
// something that needs doing before everything
.....<some business logic here>....
WebDriverManager.getInstance(DriverManagerType.CHROME).setup();
};
}
我尝试用 @Before
中的 io.cucumber.java
替换 @BeforeEach
中的 org.junit.jupiter.api
标记,但它不起作用。
我该如何解决这个问题?
解决方法
事实证明,parallism
主要是一个建议。 Cucumber 使用 JUnit5s ForkJoinPoolHierarchicalTestExecutorService
来构造一个 ForkJoinPool
。
来自 ForkJoinPool
上的文档:
对于需要单独或自定义池的应用程序,可以使用给定的目标并行级别构建 ForkJoinPool;默认情况下,等于可用处理器的数量。池尝试通过动态添加、挂起或恢复内部工作线程来维持足够的活动(或可用)线程,即使某些任务在等待加入其他任务时暂停。但是,面对阻塞的 I/O 或其他非托管同步,无法保证进行此类调整。
因此,在 ForkJoinPool
内,当一个线程因为开始与 Web 驱动程序的异步通信而阻塞时,可能会启动另一个线程来维持并行性。
由于所有线程都在等待,更多的线程被添加到池中,更多的网络驱动程序被启动。
这意味着与其依赖 ForkJoinPool
来限制网络驱动程序的数量,不如自己动手。您可以使用 Apache Commons Pool 之类的库或使用计数信号量实现基本池。
@Component
@ScenarioScope
public class ScenarioContext {
private static final int MAX_CONCURRENT_WEB_DRIVERS = 1;
private static final Semaphore semaphore = new Semaphore(MAX_CONCURRENT_WEB_DRIVERS,true);
private WebDriver driver;
public WebDriver getDriver() {
if (driver != null) {
return driver;
}
try {
semaphore.acquire();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
driver = CustomChromeDriver.getInstance();
} catch (Throwable t){
semaphore.release();
throw t;
}
return driver;
}
public void retireDriver() {
if (driver == null) {
return;
}
try {
driver.quit();
} finally {
driver = null;
semaphore.release();
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。