MockK @SpyK创建Spy报错Either mockType or objToCopy should not be null求解决
问题描述
我在测试依赖其他服务的ScoringService,想注入依赖服务UnderwritingService的Spy,实现两种场景:不打桩时调用真实方法,打桩时返回模拟响应。
我的初始化代码如下:
private lateinit var scoringService: ScoringService @SpyK private lateinit var underwritingServiceSpy: UnderwritingService ... <other variables to be injected as mocks> @BeforeEach fun setUp() { ... underwritingServiceSpy = UnderwritingService( underwritingRepositoryMock, contractServiceMock, MockTransactionHelper.SINGLETON, clock, objectMapperMock, businessServiceMock ) scoringService = ScoringService( underwritingRepositoryMock, apolloQueryExecutor, contractPricingClientSupplier, ... underwritingServiceSpy ) }
运行测试时出现错误:
io.mockk.MockKException: Either mockType or objToCopy should not be null
错误堆栈:
at io.mockk.impl.instantiation.AbstractMockFactory.spyk(AbstractMockFactory.kt:81) at io.mockk.impl.annotations.JvmMockInitializer.createSpyK(JvmMockInitializer.kt:127) at io.mockk.impl.annotations.JvmMockInitializer.assignSpyK(JvmMockInitializer.kt:117) at io.mockk.impl.annotations.JvmMockInitializer.initMock(JvmMockInitializer.kt:50) at io.mockk.impl.annotations.JvmMockInitializer.initAnnotatedMocks(JvmMockInitializer.kt:24) at io.mockk.junit5.MockKExtension.postProcessTestInstance(MockKExtension.kt:238) at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$10(ClassBasedTestDescriptor.java:377) at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:382) at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$11(ClassBasedTestDescriptor.java:377) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:310) at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735) at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734) at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762) at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:376) at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$6(ClassBasedTestDescriptor.java:289) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:288) at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$4(ClassBasedTestDescriptor.java:278) at java.base/java.util.Optional.orElseGet(Optional.java:364) at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$5(ClassBasedTestDescriptor.java:277) at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:105) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:104) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:68) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:147) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:127) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:90) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:55) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:102) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:54) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86) at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86) at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53) at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57) at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38) at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
解决方案
这个错误是因为你同时使用了@SpyK注解和手动实例化对象,MockK的注解处理器会在测试实例初始化阶段尝试创建Spy,但此时字段为null,而你手动赋值的时机在注解初始化之后,导致冲突。
修正方式有两种:
方式一:手动用spyk()函数创建Spy(推荐)
去掉@SpyK注解,先实例化真实对象,再用spyk()包装成Spy:
private lateinit var scoringService: ScoringService private lateinit var underwritingServiceSpy: UnderwritingService // 移除@SpyK注解 ... <other variables> @BeforeEach fun setUp() { // 先创建真实的UnderwritingService实例 val realUnderwritingService = UnderwritingService( underwritingRepositoryMock, contractServiceMock, MockTransactionHelper.SINGLETON, clock, objectMapperMock, businessServiceMock ) // 将真实实例包装为Spy underwritingServiceSpy = spyk(realUnderwritingService) scoringService = ScoringService( underwritingRepositoryMock, apolloQueryExecutor, contractPricingClientSupplier, ... underwritingServiceSpy ) }
方式二:通过@SpyK注解参数指定构造逻辑
保留@SpyK注解,通过constructorArgs参数传入构造所需的依赖,让MockK自动创建带真实逻辑的Spy:
private lateinit var scoringService: ScoringService @SpyK( constructorArgs = [ "underwritingRepositoryMock", "contractServiceMock", "MockTransactionHelper.SINGLETON", "clock", "objectMapperMock", "businessServiceMock" ] ) private lateinit var underwritingServiceSpy: UnderwritingService ... <other variables> @BeforeEach fun setUp() { // 无需手动实例化underwritingServiceSpy,MockK会自动处理 scoringService = ScoringService( underwritingRepositoryMock, apolloQueryExecutor, contractPricingClientSupplier, ... underwritingServiceSpy ) }
验证效果
- 不打桩时:调用
underwritingServiceSpy的方法会执行真实业务逻辑 - 需要打桩时:用
every{}指定方法返回值,示例:
@Test fun testWithStubbing() { // 打桩指定返回值 every { underwritingServiceSpy.calculateScore(any()) } returns 90 // 执行测试逻辑 val result = scoringService.evaluate(...) // 断言结果 assertEquals(90, result.score) }
内容的提问来源于stack exchange,提问作者RKA
相关产品推荐
相关产品推荐

