You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 00:55:54