泛型类使用spyk触发StackOverflowError,需保留父类调用能否解决?
Yes, you can absolutely use spyk for this scenario—this stack overflow issue is a specific edge case with how MockK handles method interception for generic parent classes when you explicitly call super. Let’s walk through why it happens and how to fix it.
Why the StackOverflowError Happens
When you create a spyk of your ChildClazz, MockK generates a proxy instance that wraps your real class. Normally, this proxy delegates calls to the real object, but when your overridden foobar method calls super.foobar(view), the proxy incorrectly routes this superclass method call back to the child’s intercepted method instead of the parent class’s actual implementation. This creates an infinite loop that eventually triggers the StackOverflowError.
Fixes to Use spyk Successfully
Option 1: Explicitly Call the Real Method with callOriginal()
By defining the behavior of the foobar method to invoke the real implementation directly, you break the loop and ensure the superclass call works as expected:
@Test fun callFoobar() { val view = mockk<SomeView>() val childClazz = spyk(ChildClazz(), recordPrivateCalls = true) // Explicitly tell MockK to use the real method implementation every { childClazz.foobar(any()) } answers { callOriginal() } childClazz.foobar(view) assertk.assert(childClazz.called).isEqualTo(true) }
This works because callOriginal() bypasses the proxy’s interception loop and executes the actual code in your ChildClazz, including the super.foobar(view) call and setting called = true.
Option 2: Use mockkClass with Relaxed Settings
If you prefer a cleaner setup, you can use mockkClass instead of spyk (it still supports private method recording) with relaxed settings to automatically delegate to real methods unless specified otherwise:
@Test fun callFoobar() { val view = mockk<SomeView>() val childClazz = mockkClass(ChildClazz::class, relaxed = true, recordPrivateCalls = true) childClazz.foobar(view) assertk.assert(childClazz.called).isEqualTo(true) }
Option 3: Directly Invoke the Superclass Method (Advanced)
If you need more control over the superclass call (e.g., to avoid invoking the entire child method), you can use reflection to call the parent class’s method directly from the test:
@Test fun callFoobar() { val view = mockk<SomeView>() val childClazz = spyk(ChildClazz(), recordPrivateCalls = true) every { childClazz.foobar(any()) } answers { // Use reflection to call the parent class's foobar method val superFoobar = ParentClazz::class.java.getDeclaredMethod( "foobar", ParentView::class.java ) superFoobar.invoke(this@spyk, firstArg()) // Execute the child's custom logic this@spyk.called = true } childClazz.foobar(view) assertk.assert(childClazz.called).isEqualTo(true) }
Handling Private Methods
All these options preserve recordPrivateCalls = true, so you can still verify or stub private methods using MockK’s private API support (e.g., verifyPrivate(childClazz) { call("privateMethod") }).
内容的提问来源于stack exchange,提问作者Denis Vodolazskiy

