我是Spock的新手,目前正在切换到它,但是我继承了许多需要重新使用的测试配置文件。每个配置文件都是一个JSON,与Spec类的名称相同。对于每种测试方法,都有一个带有参数的地图列表,例如:
LoginSpec.json:
{
"My first test": [
{
"user": "user_one",
"role": "ADMIN"
},
{
"user": "user_two",
"role": "REPORTER",
"other_param": "other"
}
],
"Some Other Test Method": [
{
"url": "/lab1",
"button_name": "Show news popup"
}
]
}
TestNG 允许我在数据提供者方法中传递测试方法名称,因此我可以根据测试类名称和测试方法名称返回映射列表。我的基类中只有一个数据提供程序方法:
public Object[][] getData(String method) {
DataReader reader = new JsonReader()
return reader.parse(packageFullName, getClass().simpleName, method)
}
此方法的结果是,我得到了一个Map数组,可以在每个测试迭代中使用。然后,我只是将此方法指定为DataProvider:
@Test(dataProvider = "getData", priority = 1)
void EULA_1(Map data) { <====
Pages.login.openLoginPage()
Pages.login.logIn(data.user) <====
...
}
这很完美:在基类中声明了一个,它会自动接收测试并提供测试数据。
问题是:是否可以在Spock测试中应用类似的方法?
我想在我的基类中有一些getData()方法,在这里我可以根据测试方法名称读取测试参数,然后将它们传递到 where 块中。
我尝试使用json阅读器,如下所示:
def "My first test"() {
setup:
println(data)
when:
...
then:
...
where:
data = dataReader.parse("JobE2E", "LoginSpec.json", "My first test")
}
此示例为我提供了必需的地图列表,但有两个问题:
总结: 实现数据提供程序以接收测试方法名称并返回地图列表的最佳方法是什么?
答案 0 :(得分:1)
您可以使用以下方法解决data
的问题:
data << dataReader.parse('JobE2E', "${getClass().name}.json", 'My first test')
它将迭代映射列表,因此每次测试迭代将仅由该映射参数化。
当前测试名称可以通过以下方式获取:
specificationContext.currentFeature.name
当前迭代名称由:
specificationContext.currentIteration.name
但是在where
部分中两者均不可访问,因为它们是在测试本身之前执行的,其中只有共享上下文中的值才可用。 因此,这里恐怕您必须手动输入测试名称。
更新:我在where
部分中找到了如何获取功能名称的解决方案。它是使用拦截器通过自己的扩展实现的。
功能详细信息容器:
class FeatureDetails {
String name
}
扩展注释:
import org.spockframework.runtime.extension.ExtensionAnnotation
import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@ExtensionAnnotation(FeatureDetailsExtension.class)
@interface ShareFeatureDetails {
}
具有内联拦截器实现的Spock扩展:
import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension
import org.spockframework.runtime.model.FeatureInfo
class FeatureDetailsExtension extends AbstractAnnotationDrivenExtension<ShareFeatureDetails> {
def featureDetails = new FeatureDetails()
@Override
void visitFeatureAnnotation(ShareFeatureDetails annotation, FeatureInfo feature) {
feature.addInterceptor({ i ->
featureDetails.name = feature.name
feature.spec.allFields.each { f ->
if (f.type == FeatureDetails.class && f.readValue(i.getInstance()) == null) {
f.writeValue(i.getInstance(), featureDetails)
}
}
i.proceed()
})
}
}
扩展名的用法示例
class DataProviderSpec extends Specification {
@Shared
FeatureDetails currentFeature
@Unroll("Test #data.a * 2 = #data.b")
@ShareFeatureDetails
def 'test'() {
when:
println data
then:
data.a * 2 == data.b
where:
data << loadData()
}
@Unroll("Test #data.a * 3 = #data.b")
@ShareFeatureDetails
def 'another test'() {
when:
println data
then:
data.a * 3 == data.b
where:
data << loadData()
}
def loadData() {
// this is hard coded example
println "${getClass().name}.${currentFeature.name}"
if ('test' == currentFeature.name) return [[a: 1, b: 2], [a: 2, b: 4]]
if ('another test' == currentFeature.name) return [[a: 3, b: 9], [a: 4, b: 12]]
return []
// ... use load from data file (JSON, YAML, XML, ...) instead:
// return dataReader.parse("${getClass().name}.json", currentFeature.name)
}
}
以及以上示例的输出:
DataProviderSpec.test
[a:1,b:2]
[a:2,b:4]
DataProviderSpec。另一项测试
[a:3,b:6]
[a:4,b:8]
第一个想法是仅在spec类中使用带注释的String featureName
字段,但是存在一个问题,其中visitFeatureAnnotation()
方法在每次调用期间都使用不同的spec实例,而每个执行loadData()
方法初审时间。
注意:您还可以使用@Unroll
批注添加说明,其中包含特定于当前迭代的值。例如:
@Unroll("Test #data.a * 2 = #data.b")
def 'test'() {
setup:
...
when:
...
then:
data.a * 2 == data.b
where:
data << getData('test')
}
def getData(String methodName) {
if ('test' == methodName) return [[a: 1, b: 2], [a: 2, b: 4]]
...
}
会产生:
测试1 * 2 = 2
测试2 * 2 = 4
答案 1 :(得分:0)
您可以使用JsonSlurper
。它基本上解析JSON并返回一个Object,它可以是List或Map(只需将其强制转换)。您可以在自己的were
块中轻松使用它(记住在那里仅使用static
或@Shared
)。
Here是Groovy中有关JSON的一些文档。
答案 2 :(得分:0)
已解决。
在BaseSpec
类中声明的以下方法在where
块的阶段自动获取当前规范的名称,并相应地从配置文件中加载参数:
protected List<Map<String, Object>> getData() {
String methodName = StackTraceUtils.sanitize(new Throwable()).stackTrace[1].methodName
FeatureInfo spec = specificationContext.currentSpec.features.find {
FeatureInfo info ->
info.dataProviders.any {
it.dataProviderMethod.name == methodName
}
}
Class className = getClass()
String packageFullName = className.package.name
String packageName = packageFullName[(packageFullName.lastIndexOf(".") + 1)..-1]
TestDataReader reader = new JsonReader()
return reader.parse(packageName, className.simpleName, spec.name)
}
类中的用法,它是BaseSpec
类的子类:
def "My custom name spec"() {
when:
...
then:
...
where:
data << getData()
}