Java jvm线程安全吗?
我想在多个线程中运行相同的cumber测试。更具体地说,我有一套功能,在一个线程中运行这些功能就可以了。我使用JSON格式化程序记录每个步骤的运行时间。现在我想做负载测试。我更关心多线程环境中每个功能/步骤的运行时间。所以我创建了多个线程,每个线程运行在同一个功能集上。每个线程都有自己的JSON报告。这在理论上可能吗 由于某些项目设置原因,我无法使用JUnit runner。因此,我不得不求助于CLI方法:Java jvm线程安全吗?,java,multithreading,gradle,cucumber-jvm,gpars,Java,Multithreading,Gradle,Cucumber Jvm,Gpars,我想在多个线程中运行相同的cumber测试。更具体地说,我有一套功能,在一个线程中运行这些功能就可以了。我使用JSON格式化程序记录每个步骤的运行时间。现在我想做负载测试。我更关心多线程环境中每个功能/步骤的运行时间。所以我创建了多个线程,每个线程运行在同一个功能集上。每个线程都有自己的JSON报告。这在理论上可能吗 由于某些项目设置原因,我无法使用JUnit runner。因此,我不得不求助于CLI方法: long threadId = Thread.currentThread
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();
我试着为每一根黄瓜创建一条单独的线。问题是,只有一个线程具有有效的JSON报告。所有其他线程只创建空JSON文件。这是Cucumber设计的还是我遗漏了什么?目前还没有——这是你观察到的。我还没有找到任何方法按场景进行并行化
这是一个关于穷人的并发性的好例子。只需运行多个命令,每个命令选择测试的不同子集——按功能或标记。我会分叉一个新的JVM(就像JUnit驱动程序那样),而不是尝试线程,因为cucumber不是为此而设计的。你必须自己平衡它们,然后找出如何组合报告。(但至少问题是合并报表而不是损坏的报表。)我们已经研究了Gradle和Groovy下的多线程cucumber测试,使用了优秀的。我们有650个用户界面测试和计数 在多线程中运行JVM没有遇到任何明显的问题,但是多线程也没有像我们希望的那样提高性能 我们在单独的线程中运行每个功能文件。有一些细节需要注意,比如将来自不同线程的Cumber报告拼接在一起,并确保我们的step代码是线程安全的。我们有时需要在步骤之间存储值,因此我们使用一个对线程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)]
}
}
下面是Gradle任务代码的要点,该代码使用GPAR运行多线程测试:
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()
def group=new DefaultPGroup(maxSimultaneousThreads())
def workUnits=features.collect{File featureFile->
组任务{
试一试{
javaexec{
main=“cumber.api.cli.main”
...
args=[
...
“--plugin',“json:$unitReportDir/${featureFile.name}.json”,
...
“--glue”、“src/test/groovy/steps”,
“路径/to/$featureFile”
]
}
}捕获(执行异常){
++努弗罗斯
stackTraces假设您可以通过使用以下Maven POM配置并行运行Cucumber JVM测试:
org.apache.maven.plugins
maven surefire插件
2.14
验收试验
集成测试
测试
${surefire.fork.count}
假的
-Duser.language=en
-Xmx1024m
-XX:MaxPermSize=256m
-文件编码=UTF-8
假的
**/*上课时
真的
在上面的代码片段中,您可以看到maven surefire插件用于运行我们的验收测试–任何以*结尾的类都将作为JUnit测试类运行。多亏了JUnit,使测试并行运行现在是设置forkCount配置选项的一个简单例子。在示例项目中,该选项设置为5,这意味着我们可以n一次最多运行5个线程(即,5个运行程序类)。如果您可以找到一种方法,让cucumber根据给定的标记为您想要运行的所有场景输出场景位置(即feature_file_path:line_Number_in_feature_file),那么您可以使用GPAR和gradle并行运行场景。
步骤1:在第一个gradle任务中,我们将使用上述解决方案生成一个文本文件(比如scenarios.txt),其中包含我们要执行的所有场景的位置
步骤2:接下来,将步骤1中生成的scenarios.txt的内容提取到groovy列表中,比如scenariosList
步骤3:再创建一个任务(javaExec),这里我们将使用gparswithpool与scenariosList.eachParallel结合使用,并使用cucumber主类和其他cucumberOptions并行运行这些场景。PS:这里我们将提供场景位置作为选项“features”的值所以cucumber将只运行这个场景。也不需要提供任何标记名,因为我们已经有了需要执行的场景列表
注意:您需要使用具有高配置的机器,如Linux服务器,因为每个场景都会创建一个新的jvm实例,并且可能使用Saucelabs之类的云服务来执行场景。这样,您就不必担心基础设施
步骤4:这是最后一步。步骤3中的每个场景都将生成一个json输出文件。您必须根据功能名称整理输出,以便为每个功能文件生成一个json文件
这个解决方案听起来有点复杂,但只要付出正确的努力,就可以产生显著的结果。我创建了这样一个解决方案:并行运行功能文件。。但是拉请求没有得到批准,因为这是基于JUnitRunner的
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.14</version>
<executions>
<execution>
<id>acceptance-test</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<forkCount>${surefire.fork.count}</forkCount>
<refuseForks>false</reuseForks>
<argLine>-Duser.language=en</argLine>
<argLine>-Xmx1024m</argLine>
<argLine>-XX:MaxPermSize=256m</argLine>
<argLine>-Dfile.encoding=UTF-8</argLine>
<useFile>false</useFile>
<includes>
<include>**/*AT.class</include>
</includes>
<testFailureIgnore>true</testFailureIgnore>
</configuration>
</execution>
</executions>
</plugin>