Reputation: 5043
I've got some Java classes that I've written Spock tests for. When I try to verify the exact object used in an argument, the test fails.
def "all events are published to subscribers"() {
given:
EventSubscriber subscriber = Mock()
when:
subscriber.shouldRegister() >> true
subscriber.interests() >> new HashSet<Intent>(Arrays.asList(COUNTER, GAUGE))
publisher.subscribe(subscriber)
def event = publisher.newEvent("test", COUNTER)
event.start()
def child = event.newChild("child", GAUGE)
event.finish()
then:
// 2 * subscriber.onCreate(event)
// 1 * subscriber.onStart(event, event.start)
// 1 * subscriber.onEnd(event, event.end)
// 1 * subscriber.onChild(event, child)
2 * subscriber.onCreate(_)
1 * subscriber.onStart(_, _)
1 * subscriber.onEnd(_, _)
1 * subscriber.onChild(_, _)
}
The commented out lines fail with
groovy.lang.MissingPropertyException: No such property: event for class: io.hypi.faf.api.EventPublisherTest
at io.hypi.faf.api.EventPublisherTest.all events are published to subscribers(EventPublisherTest.groovy:28)
http://spockframework.org/spock/docs/1.3/all_in_one.html#_argument_constraints seems to suggests I can just pass a value in. The first example is a string but I presume type doesn't matter.
What have I done wrong with this?
Spock: 1.3 Groovy: 2.5 groovy-test-junit5-2.5.7 JDK: 11
Upvotes: 2
Views: 1661
Reputation: 5031
Object instances used in the interaction testing should be defined in the given
section.
This would have work:
def 'all events are published to subscribers'() {
given:
EventSubscriber subscriber = Mock()
subscriber.shouldRegister() >> true
subscriber.interests() >> new HashSet<Intent>(Arrays.asList(COUNTER, GAUGE))
publisher.subscribe(subscriber)
def event = publisher.newEvent('test', COUNTER)
def child = event.newChild('child', GAUGE)
when:
event.start()
event.finish()
then:
2 * subscriber.onCreate(event)
1 * subscriber.onStart(event, event.start)
1 * subscriber.onEnd(event, event.end)
1 * subscriber.onChild(event, child)
}
But then there will be a problem with testing of invocation of onCreate()
and onChild()
because these calls are then not in the when
section and only onStart()
and onEnd()
can match.
I don't have your classes definitions to see details but I guess that event.start
and event.end
fields are initialized in start()
and finish()
methods and then 1 * onStart(event, event.start)
and 1 * onEnd(event, event.end)
test will not match because invocation test will be done in the phase when the event
has different start
and end
values. So then only 1 * onStart(event, _)
and 1 * onEnd(event, _)
tests will only match.
Look on this simple example which works and is similar to your case but with the difference that the event
instance is prepared in the given
section and not modified in the when
so it will pass the test:
class InvocationWithArgumentsSpec extends Specification {
void 'Invocation test works with arguments other then String'() {
given:
def subscriber = Mock(Subscriber)
def simplePublisher = new Publisher()
simplePublisher.subscribe(subscriber)
def event = new SimpleEvent(name: 'test')
when:
simplePublisher.fireEvent(event)
then:
1 * subscriber.onEvent(event)
}
}
interface Subscriber {
void onEvent(SimpleEvent event)
}
class Publisher {
Subscriber subscriber
void subscribe(Subscriber subscriber) {
this.subscriber = subscriber
}
void fireEvent(SimpleEvent event) {
subscriber.onEvent(event)
}
}
class SimpleEvent {
String name
}
Update: But there is a workaround for you. It is not so nice but it works and tests that correct events are passed into subscriber methods (I guess that event
has something like name
):
def 'all events are published to subscribers'() {
given:
EventSubscriber subscriber = Mock()
subscriber.shouldRegister() >> true
subscriber.interests() >> new HashSet<Intent>(Arrays.asList(COUNTER, GAUGE))
publisher.subscribe(subscriber)
when:
def event = publisher.newEvent('test', COUNTER)
event.start()
event.newChild('child', GAUGE)
event.finish()
then:
1 * subscriber.onCreate({ it.name == 'test' })
1 * subscriber.onCreate({ it.name == 'child' })
1 * subscriber.onStart({ it.name == 'test' }, !null)
1 * subscriber.onEnd({ it.name == 'test' }, !null)
1 * subscriber.onChild({ it.name == 'test' }, { it.name == 'child' })
}
Upvotes: 2