Groovy 是否可以动态生成Spock测试方法?

Groovy 是否可以动态生成Spock测试方法?,groovy,spock,Groovy,Spock,我有很多重复的测试,我想以某种方式将它们抽象出来。助手方法很有用,但在一定程度上是有用的。如果我能以某种方式在规范中以编程方式生成测试方法,我将能够删除大量复制粘贴代码 具体地说,我正在测试RPC层的方法。如果没有以某种方式调用,大多数方法都会失败。所以我有斯波克测试方法,比如: def observer = Mock(StreamObserver) def rpc = new UserServiceRpc() def "draftToOutbox: should fail when call

我有很多重复的测试,我想以某种方式将它们抽象出来。助手方法很有用,但在一定程度上是有用的。如果我能以某种方式在
规范中以编程方式生成测试方法,我将能够删除大量复制粘贴代码

具体地说,我正在测试RPC层的方法。如果没有以某种方式调用,大多数方法都会失败。所以我有斯波克测试方法,比如:

def observer = Mock(StreamObserver)
def rpc = new UserServiceRpc()

def "draftToOutbox: should fail when called without parent"() {

    def request = draftToOutboxRequest().toBuilder().clearParent().build()

    when:
    rpc.draftToOutbox(request, observer)

    then:
    1 * observer.onError(_)
    1 * observer.onCompleted()
}

def "outboxToDraft: should fail when called without parent"() {

    def request = outboxToDraftRequest().toBuilder().clearParent().build()

    when:
    rpc.outboxToDraft(request, observer)

    then:
    1 * observer.onError(_)
    1 * observer.onCompleted()
}

不幸的是,我对Groovy的元编程(编译时AST转换)功能不太熟悉,也不确定这是如何实现的,这是否可能,这是否是一个好主意。一方面,我有一些复制粘贴,但另一方面,我的测试非常可读。不过,我倾向于两全其美:不使用复制粘贴的可读性。

在这种情况下,您可以使用一些groovy工具,如:

@Unroll
def "#method: should fail when called without parent"() {
    given:
    def request = "$methodToGetRequest"().toBuilder().clearParent().build()
    when:
    rpc."$methodToTest"(request, observer)
    then:
    1 * observer.onError(_)
    1 * observer.onCompleted()

    where:
    methodToTest    | methodToGetRequest
    'draftToOutbox' | 'draftToOutboxRequest'
    'outboxToDraft' | 'outboxToDraftRequest'
}
编辑

使用方法指针的示例:

@Shared
String subjectToTest

def setupSpec() {
    subjectToTest = "sTring To Test"
}

def "test using method pointers"() {
    when:
    def result = methodPointer()
    then:
    result == expectecResult
    where:
    expectecResult   | methodPointer
    'STRING TO TEST' | subjectToTest.&toUpperCase
    'string to test' | subjectToTest.&toLowerCase
}

在这种情况下,您可以使用以下groovy工具:

@Unroll
def "#method: should fail when called without parent"() {
    given:
    def request = "$methodToGetRequest"().toBuilder().clearParent().build()
    when:
    rpc."$methodToTest"(request, observer)
    then:
    1 * observer.onError(_)
    1 * observer.onCompleted()

    where:
    methodToTest    | methodToGetRequest
    'draftToOutbox' | 'draftToOutboxRequest'
    'outboxToDraft' | 'outboxToDraftRequest'
}
编辑

使用方法指针的示例:

@Shared
String subjectToTest

def setupSpec() {
    subjectToTest = "sTring To Test"
}

def "test using method pointers"() {
    when:
    def result = methodPointer()
    then:
    result == expectecResult
    where:
    expectecResult   | methodPointer
    'STRING TO TEST' | subjectToTest.&toUpperCase
    'string to test' | subjectToTest.&toLowerCase
}


由于Spock严重依赖于AST转换,我认为如果不构建自己的AST转换,就不可能生成测试方法。是的,这基本上是我问题的关键。但我肯定不是第一个遇到这个问题的人。我可以尝试重新构建它,这样我只需要在一个地方测试它,但在这种情况下这并不容易,因为我使用的是GRPCJava,它不允许我在RPC方法之前配置过滤器。我真的很想对所有方法进行测试@那么你认为我必须写一个编译时AST转换?您认为结果可读吗?听起来您还可以生成源代码作为替代方法。这可能比深入研究斯波克的AST更容易。Groovy AST转换通常读起来像埃及象形文字。由于斯波克严重依赖AST转换,我认为如果不构建自己的AST转换来生成测试方法,那么生成测试方法是不可能的。是的,这基本上是我问题的关键。但我肯定不是第一个遇到这个问题的人。我可以尝试重新构建它,这样我只需要在一个地方测试它,但在这种情况下这并不容易,因为我使用的是GRPCJava,它不允许我在RPC方法之前配置过滤器。我真的很想对所有方法进行测试@那么你认为我必须写一个编译时AST转换?您认为结果可读吗?听起来您还可以生成源代码作为替代方法。这可能比深入研究斯波克的AST更容易。Groovy AST转换通常读起来像埃及象形文字。是的,那会很漂亮,除非我正在测试
void
methods在您的情况下,给定的块将只是
rpc。“$methodName”(请求,观察者)
在expect块中,您将检查模拟迭代。我认为我无法在数据表的expect块中进行交互测试。我需要根据我的问题测试与观察者的交互。哦,那么在“where”表之前的“expect”块中,我可以断言观察者的交互?我要试试这个!谢谢。我更新了答案,给你一个确切的例子。希望它有帮助。是的,那会很漂亮,除非我正在测试
void
methods在您的情况下,给定的块将是
rpc.“$methodName”(请求,观察者)
,在expect块中,您将检查模拟迭代。我不认为我可以在数据表的expect块中进行交互测试。我需要根据我的问题测试与观察者的交互。哦,那么在“where”表之前的“expect”块中,我可以断言观察者的交互?我要试试这个!谢谢。我更新了答案,给你一个确切的例子。希望能有帮助。