我想在多个线程中运行相同的 Cucumber 测试。更具体地说,我有一组功能,在一个线程中运行这些功能效果很好。我使用 JSON 格式化程序记录每个步骤的运行时间。现在我想做负载测试。我更关心多线程环境下每个特性/步骤的运行时间。所以我创建了多个线程,每个线程都运行在同一个特性集上。每个线程都有自己的 JSON 报告。这在理论上可能吗?
由于某些项目设置原因,我无法使用 JUnit 运行器。所以我不得不求助于 CLI 方式:
long threadId = Thread.currentThread().getId();
String jsonFilename = String.format("json:run/cucumber%d.json", threadId);
String argv[] = new String[]{
"--glue",
"com.some.package",
"--format",
jsonFilename,
"d:\\features"};
// Do not call Main.run() directly. It has a System.exit() call at the end.
// Main.run(argv, Thread.currentThread().getContextClassLoader());
// Copied the same code from Main.run().
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
RuntimeOptions runtimeOptions = new RuntimeOptions(new Env("cucumber-jvm"), argv);
ResourceLoader resourceLoader = new MultiLoader(classLoader);
ClassFinder classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader);
Runtime runtime = new Runtime(resourceLoader, classFinder, classLoader, runtimeOptions);
runtime.writeStepdefsJson();
runtime.run();
我尝试为每个 Cucumber 运行创建一个单独的线程。问题是,只有一个线程具有有效的 JSON 报告。所有其他线程只创建空的 JSON 文件。这是 Cucumber 设计的还是我遗漏了什么?
最佳答案
我们使用出色的 GPars library 研究了 Gradle 和 Groovy 下的多线程 Cucumber 测试。 .我们有 650 个 UI 测试并且还在增加。
我们在多线程中运行 cucumber-JVM 时没有遇到任何明显的问题,但多线程也没有像我们希望的那样提高性能。
我们在单独的线程中运行每个功能文件。有一些细节需要注意,比如将来自不同线程的 cucumber 报告拼接在一起,并确保我们的步骤代码是线程安全的。我们有时需要在步骤之间存储值,因此我们使用以线程 id 为键的 concurrentHashMap 来存储这种数据:
class ThreadedStorage {
static private ConcurrentHashMap multiThreadedStorage = [:]
static private String threadSafeKey(unThreadSafeKey) {
def threadId = Thread.currentThread().toString()
"$threadId:$unThreadSafeKey"
}
static private void threadSafeStore(key, value) {
multiThreadedStorage[threadSafeKey(key)] = value
}
def static private threadSafeRetrieve(key) {
multiThreadedStorage[threadSafeKey(key)]
}
}
下面是使用 GPars 运行多线程测试的 Gradle 任务代码的要点:
def group = new DefaultPGroup(maxSimultaneousThreads())
def workUnits = features.collect { File featureFile ->
group.task {
try {
javaexec {
main = "cucumber.api.cli.Main"
...
args = [
...
'--plugin', "json:$unitReportDir/${featureFile.name}.json",
...
'--glue', 'src/test/groovy/steps',
"path/to/$featureFile"
]
}
} catch (ExecException e) {
++noOfErrors
stackTraces << [featureFile, e.getStackTrace()]
}
}
}
// ensure all tests have run before reporting and finishing gradle task
workUnits*.join()
我们发现我们需要以执行时间的倒序呈现特征文件以获得最佳结果。
结果是在 i5 CPU 上有 30% 的改进,同时线程数超过了 4 个,这有点令人失望。
我认为线程对于我们硬件上的多线程来说太重了。超过一定数量的线程时,CPU 缓存未命中过多。
使用像 Amazon SQS 这样的线程安全工作队列在不同实例上同时运行现在看来是一个很好的前进方向,尤其是因为它不会遇到线程安全问题(至少在测试框架方面不会)。
由于工作场所的安全限制,我们在 i7 硬件上测试这种多线程方法并非易事,但我很想知道与具有更大 CPU 缓存和更多物理内核的 i7 相比如何。
关于java - Cucumber-jvm线程安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25005057/