Reputation: 17066
How can an annotated class be mocked (or spied) in Spock so that logic which relies on the annotation can be asserted?
A contrived example in Groovy (could be Java too) shows a method that checks for a specific annotation value in a collection of objects. Imagine more complex logic performed after filtering by annotation, which I'd like to assert by mocking objects in the collection.
@FooAnnotation('FOOBAR')
class MainGroovy {
def findFOOBARs(Object... candidates) {
candidates.findAll{ it.class.getAnnotation(FooAnnotation)?.value() == 'FOOBAR' }
//Do something with the filtered objects.
}
}
Passing a Spy fails the annotation filter, so nothing can be asserted about any subsequent logic.
@Unroll
def test() {
given:
def foobars = mg.findFOOBARs(mg, new Object(), 'STRING')
expect:
foobars.size() == 1
where:
mg << [new MainGroovy(), Spy(MainGroovy)]
}
Upvotes: 2
Views: 4316
Reputation: 3266
I might be not aware of something, but I've done a quick research of this use case. Looks like it's impossible to retain annotations from mocked/spied classes using Spock's Mock
or Spy
. If we look into how Spock creates mocks/spies of a class when byte-buddy is used, we'll see that it subclasses the original type. If we look deeper into how byte-buddy works by default, then we will see that it doesn't retain annotations of the original type unless configured otherwise. By default, it just uses InstrumentedType's Default Factory with subclass method ignoring annotations' retention.
I haven't found any issues related to the annotations retention on Spock's GitHub yet. A fix on Spock side might look quite trivial but I'm not sure about this. Better to ask on their GitHub.
As a quite ugly workaround for simple cases, you can try Mockito
spy/mock in a Spock spec
Upvotes: 2