Issue
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))
}
}
Solution
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
.
Answered By - Leonard Brünings