我在 UserManager 类中有一个 @Step 注释的 Kotlin 方法,它向 SQL 数据库添加一个条目:
@Step("Insert record")
fun insertAll(
profileId: String,
) {
userDAO.insertAll(
profileId = profileId
)
}
如果我在单线程模式下从 @Test 注解的方法调用它,那么一切都会正常工作。
for (i in 0..usersCount) {
userManager.insertAll(
profileId
)
}
但是如果我使用parallelStream(),就会发生奇怪的事情。
(0..usersCount)
.toList()
.parallelStream()
.forEach {
userManager.insertAll(
profileId
)
}
一方面,代码继续工作,我的工作效率得到提高,但另一方面,日志中充满了相同类型的错误消息:
15:36:27.762 ERROR [AllureLifecycle] : Could not start step: no test case running
15:36:27.764 ERROR [AllureLifecycle] : Could not update step: no step running
15:36:27.764 ERROR [AllureLifecycle] : Could not stop step: no step running
依赖关系:
implementation("io.qameta.allure:allure-java-commons:2.21.0")
我做错了什么?
parallelStream
的问题是它使用共享线程池,该线程池可能有也可能没有当前执行的测试或步骤的上下文。
为了解决这个问题,有一个 API 方法
Allure.getLifecycle().setCurrentTestCase(String)
可用。因此,要解决这个问题,您需要从主线程获取当前测试用例 UUID 并将其设置在工作线程中(在使用任何其他 Allure API 之前):
val currentTestCase = Allure.getLifecycle().currentTestCase.orElseThrow()
(0..usersCount)
.toList()
.parallelStream()
.forEach {
Allure.getLifecycle().setCurrentTestCase(currentTestCase);
userManager.insertAll(
profileId
)
}
但是,如果您需要围绕该代码创建一个包装步骤,它将被忽略(因为我们只将测试上下文设置到工作线程中)。不幸的是,目前还没有可用的方法来设置当前步骤上下文。因此,要解决此问题,您需要使用低级(生命周期)API 来创建步骤:
Allure.step("child steps run in parallel", ThrowableRunnableVoid {
// get the current step context
val parentUuid = Allure.getLifecycle()
.currentTestCaseOrStep.orElseThrow()
(0..usersCount)
.toList()
.parallelStream()
.forEach {
val uuid = UUID.randomUUID().toString()
// parentUuid is essential. Instead of using value from ThreadLocal,
// provide parent context directly
Allure.getLifecycle().startStep(
parentUuid, uuid, StepResult()
.setName("userManager.insertAll(${profileId})")
)
try {
// run your code here
userManager.insertAll(
profiled
)
// if no exception, update the step
Allure.getLifecycle()
.updateStep(uuid) { step: StepResult ->
step.setStatus(
Status.PASSED
)
}
} catch (e: Exception) {
// process the exception
Allure.getLifecycle()
.updateStep(uuid) { s: StepResult ->
s
.setStatus(
ResultsUtils.getStatus(e).orElse(Status.BROKEN)
)
.setStatusDetails(ResultsUtils.getStatusDetails(e).orElse(null))
}
ExceptionUtils.sneakyThrow<RuntimeException>(e)
} finally {
// finally stop the step
Allure.getLifecycle().stopStep(uuid)
}
}