簡體   English   中英

Spock中的TestNG數據提供程序的類似物

[英]Analog of TestNG data provider in Spock

我是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")
}

此示例為我提供了所需的地圖列表,但有兩個問題:

  1. 此處的數據 -是地圖的完整列表,而不是每次迭代都有一張地圖;
  2. 我被迫明確輸入測試方法,類等的名稱。

總結:實現數據提供程序的最佳方法是什么,該數據提供程序將接收測試方法名稱並返回映射列表?

您可以使用以下方法解決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

您可以使用JsonSlurper 它基本上解析JSON並返回一個Object,它可以是List或Map(只需將其強制轉換)。 您可以輕松地在使用它were塊(記住,只能使用static@Shared在那里)。

是Groovy中有關JSON的一些文檔。

解決了。

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()
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM