ketrab321
ketrab321

Reputation: 591

MissingPropertyException in Spock test

I have problem with test written in groovy (using Spock as a framework). While I try fetch property in then block and then use it in interaction I receive No such property: updatedValue for class: my.test.class.MyTestClass on the interaction with event publisher. Can anyone tell why is that?

class MyTestClass extends Specification {


@Autowired
private MyClass sut

@Autowired
private MyClassRepository myClassRepository


@SpringBean
private EventPublisher eventPublisher = Mock()


def "Should do something"() {
    given:
    //some data
    def someId = "someId"
    def event = new SomeEventObject()

    when:
    sut.handle(event)

    then:
    def updatedValue = myClassRepository.findTestClass(someId)
    updatedTestClass.cash == 100

    1 * eventPublisher.publishEvent(new SomeNextEvent(updatedValue))
}
}

Upvotes: 1

Views: 1188

Answers (1)

Leonard Brünings
Leonard Brünings

Reputation: 13222

You are encountering a side-effect of Spock's magic.

Your method basically gets transformed to this:

public void $spock_feature_0_0() {
        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
        java.lang.Object someId = 'someId'
        java.lang.Object event = new apackage.SomeEventObject()
        this.getSpecificationContext().getMockController().enterScope()
        this.getSpecificationContext().getMockController().addInteraction(new org.spockframework.mock.runtime.InteractionBuilder(25, 9, '1 * eventPublisher.publishEvent(new SomeNextEvent(updatedValue))').setFixedCount(1).addEqualTarget(eventPublisher).addEqualMethodName('publishEvent').setArgListKind(true, false).addEqualArg(new apackage.SomeNextEvent(updatedValue)).build())
        sut.handle(event)
        this.getSpecificationContext().getMockController().leaveScope()
        java.lang.Object updatedValue = myClassRepository.findTestClass(someId)
        try {
            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'updatedTestClass.cash == 100', 23, 13, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), updatedTestClass).cash) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 100)))
        }
        catch (java.lang.Throwable throwable) {
            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'updatedTestClass.cash == 100', 23, 13, null, throwable)}
        finally {
        }
        this.getSpecificationContext().getMockController().leaveScope()
    }

If you look closely, you can see that the interaction definition was moved before the when block, but the findTestClass() call stayed behind. that is why you get the missing property exception.

The solution is to either move the lookup to the given block, or if that is not possible, to use argument capturing and then check afterwards.

given:
def capturedEvent

when:
...

then:
1 * eventPublisher.publishEvent(_) >> { capturedEvent = it[0} }

and:

def updatedValue = myClassRepository.findTestClass(someId)
capturedEvent instanceof SomeNextEvent
capturedEvent.value == updatedValue 

You can take a look at the transformed code yourself in the groovy web console by clicking on Inspect AST.

Upvotes: 1

Related Questions