MissingPropertyException thrown in spock test where block
Asked Answered
M

1

8

I have been using spock for a while to unit test my java project, and have ran into a problem. I have a utility method to get a parameter from a http request, or an empty string if the http request is null and am trying to test it with spock. My test looks like this:

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"
    }
}

However, when I run this test, I get the following error:

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)

After doing some research, I understand that the where block is run before the given block, hence the error, but was just wondering if there was a workaround?

I know that to use a variable from outside the test, I would need to annotate the variable with the @Shared annotation, which seems bad practice to me. Every test should run completely separate from the others, so don't really want to have an object that keeps it's state between tests.

Is it possible to setup Mock objects to be returned from the where block any other way?

Moua answered 17/7, 2015 at 10:31 Comment(6)
Have you tried moving HttpServletRequest mockHttpRequest = Mock() to a setup block at the start of your test?Susurrous
@Susurrous Thanks for the suggest. I just tried replacing the given block with a setup block if that's what you mean, and got the same result.Moua
Searching around, I think you need to move your mocked class out into a @Shared class level fieldSusurrous
ie: code.google.com/p/spock/issues/detail?id=15#c4Susurrous
@Susurrous yeah I had seen the @Shared annotation before, but as one of the answers says 'The downside is that a and o are in a sense defined in the wrong scope and could be used by other feature methods as well.' As I have said in my edit, this seems like bad practice to me, but if there is no alternative, I will have to look into it.Moua
That article actually led me to a more elegant, (if lengthy solution, so thank you very much. :)Moua
M
7

Following tim_yates suggestion to take a look at https://code.google.com/p/spock/issues/detail?id=15#c4, I found a fairly elegant solution that doesn't involve using the @Shared annotation. The test definition now looks like this:

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"]
    }
}
Moua answered 17/7, 2015 at 11:18 Comment(1)
I guess you may move the block which creates list of mocked values for httpRequest into a separate static method and only call it in where block. Then it should be cleaner and more readable, something like httpRequest << mockHttpRequest(), where the mockHttpRequest() is a static method returning [null, mockHttpRequest]Acadia

© 2022 - 2024 — McMap. All rights reserved.