Mykola Gurov
11/14/2021, 2:27 PM@Primary
bean - a convenient technique to spy on Beans and inject "strange" behaviors.
All works good till we hit Spring AOP-proxied beans, e.g @Cacheable
in my example. Some funky interactions are happening there and we end up seeing this exception 🧵:Caused by: io.mockk.MockKException: no answer found for: TargetSource(child of #1#2).getTarget()
at io.mockk.impl.stub.MockKStub.defaultAnswer(MockKStub.kt:93) ~[mockk-1.12.0.jar:na]
at io.mockk.impl.stub.MockKStub.answer(MockKStub.kt:42) ~[mockk-1.12.0.jar:na]
at io.mockk.impl.recording.states.AnsweringState.call(AnsweringState.kt:16) ~[mockk-1.12.0.jar:na]
at io.mockk.impl.recording.CommonCallRecorder.call(CommonCallRecorder.kt:53) ~[mockk-1.12.0.jar:na]
at io.mockk.impl.stub.MockKStub.handleInvocation(MockKStub.kt:266) ~[mockk-1.12.0.jar:na]
at io.mockk.impl.instantiation.JvmMockFactoryHelper$mockHandler$1.invocation(JvmMockFactoryHelper.kt:23) ~[mockk-1.12.0.jar:na]
at io.mockk.proxy.jvm.advice.Interceptor.call(Interceptor.kt:21) ~[mockk-agent-jvm-1.12.0.jar:na]
at io.mockk.proxy.jvm.advice.BaseAdvice.handle(BaseAdvice.kt:42) ~[mockk-agent-jvm-1.12.0.jar:na]
at io.mockk.proxy.jvm.advice.jvm.JvmMockKProxyInterceptor.interceptNoSuper(JvmMockKProxyInterceptor.java:45) ~[mockk-agent-jvm-1.12.0.jar:na]
at org.springframework.aop.TargetSource$Subclass0.getTarget(Unknown Source) ~[spring-aop-5.3.12.jar:5.3.12]
@Component
class Service() {
@Cacheable("response_cache")
fun respondCached(input: String) = prefix + "_" + Instant.now().toEpochMilli()
}
@Configuration
class InjectSpiesConfiguration {
@Bean
@Primary
fun spiedService(service: Service): Service {
val spyk = spyk(service)
return spyk
}
}
@SpringBootTest
class MockkSpykingTest(
@Autowired private val spiedService: Service
) {
@Test
fun `hardcoded unwrapping`() {
//NB: no failures when the following line is commented.
every { spiedService.respondCached("empty") } returns ""
val firstUnwrap = (spiedService as Advised).targetSource.target
val secondUnwrap = (firstUnwrap as Advised).targetSource.target
SoftAssertions.assertSoftly {
it.assertThat(secondUnwrap).isNotInstanceOf(Advised::class.java)
it.assertThat(secondUnwrap).isInstanceOf(Service::class.java)
}
}
}
AopTestUtils.getUltimateTargetObject(service)
) but still - why does the mock's spy fails that invocation instead of preserving transparent behavior for non-intercepted calls?Mattia Tommasone
11/14/2021, 9:16 PMMykola Gurov
11/15/2021, 4:54 PM