在Spock测试的where块中抛出MissingPropertyException异常。

7

我已经使用Spock一段时间来对我的Java项目进行单元测试,但遇到了一个问题。我有一个实用方法来从HTTP请求中获取参数,如果HTTP请求为空,则返回空字符串,并试图使用Spock对其进行测试。我的测试如下:

package foo.bar.test

import foo.bah.HttpRequestPropertyLoader
import spock.lang.Unroll
import javax.servlet.http.HttpServletRequest
import spock.lang.Specification

class HttpRequestPropertyLoaderTest extends Specification {

    HttpRequestPropertyLoader subjectUnderTest
    def result

    def setup() {
        subjectUnderTest = new HttpRequestPropertyLoader()
    }

    @Unroll("When my http request is #nullOrNot then when I get parameter from it the response=#response" )
    def "Test load data from request"() {
        given:
        HttpServletRequest mockHttpRequest = Mock()
        mockHttpRequest.getAttribute("foo") >> "bar"
        when:
        result = subjectUnderTest.loadStringFromHttpRequest(httpRequest, "foo")
        then:
        result == response
        where:
        httpRequest     | response | nullOrNot
        null            |  ""      | "null"
        mockHttpRequest | "bar"    | "not null"
    }
}

然而,当我运行此测试时,我会得到以下错误:

groovy.lang.MissingPropertyException: No such property: mockHttpRequest for class: foo.bar.test.HttpRequestPropertyLoaderTest at foo.bar.test.HttpRequestPropertyLoaderTest.Test load data from request(HttpRequestPropertyLoaderTest.groovy)

经��一些研究,我了解到where块是在given块之前运行的,因此出现了错误,但我只是想知道是否有解决的方法?

我知道要使用来自测试外部的变量,需要使用@Shared注释对变量进行注释,但这似乎是不好的做法。每个测试应该完全独立于其他测试运行,因此不希望有一个对象在测试之间保持其状态。

是否有其他方式设置Mock对象以从where块返回?


你尝试过将HttpServletRequest mockHttpRequest = Mock()移动到测试开始处的setup块吗? - tim_yates
@tim_yates 感谢您的建议。我刚刚尝试用设置块替换给定的块,如果这是您的意思,并得到了相同的结果。 - Ben Green
搜索一番后,我认为你需要将模拟类移动到一个@Shared类级别字段中。 - tim_yates
1
请访问以下网址以获取完整的翻译内容:https://code.google.com/p/spock/issues/detail?id=15#c4 - tim_yates
@tim_yates 是的,我之前看过 @Shared 注解,但正如其中一个答案所说:“缺点是 a 和 o 在某种意义上在错误的作用域中定义,并且也可能被其他特性方法使用。”正如我在我的编辑中所说的那样,这对我来说似乎是不好的实践,但如果没有其他选择,我将不得不研究它。 - Ben Green
那篇文章实际上让我找到了一种更优雅的(虽然有点冗长)解决方案,非常感谢。 :) - Ben Green
1个回答

6

根据tim_yates的建议,查看https://code.google.com/p/spock/issues/detail?id=15#c4,我找到了一种相当优雅的解决方案,它不涉及使用@Shared注释。现在测试定义如下:

package foo.bar.test

import foo.bah.HttpRequestPropertyLoader
import spock.lang.Unroll
import javax.servlet.http.HttpServletRequest
import spock.lang.Specification

class HttpRequestPropertyLoaderTest extends Specification {

    HttpRequestPropertyLoader subjectUnderTest
    def result

    def setup() {
        subjectUnderTest = new HttpRequestPropertyLoader()
    }

    @Unroll("When my http request is #nullOrNot then when I get parameter from it the response=#response" )
    def "Test load data from request"() {
        when:
        result = subjectUnderTest.loadStringFromHttpRequest(httpRequest, "foo")
        then:
        result == response
        where:
        httpRequest << {
            HttpServletRequest mockHttpRequest = Mock()
            mockHttpRequest.getAttribute("foo") >> "bar"
            [null, mockHttpRequest]
        }()
        response << ["", "bar"]
        nullOrNot << ["null", "not null"]
    }
}

我猜你可以将创建 httpRequest 模拟值列表的代码块移动到一个单独的 static 方法中,并且只在 where 块中调用它。这样代码会更加清晰易读,类似于 httpRequest << mockHttpRequest(),其中 mockHttpRequest() 是一个返回 [null, mockHttpRequest] 的静态方法。 - topr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接