使用MockK模拟Gradle项目时存根方法未调用问题排查
解决Gradle任务单元测试中MockK存根不生效的问题
问题出在你用spyk(ProjectBuilder.builder().build())创建的Project对象上——Gradle的ExtensionContainer.getByType()方法是final方法,MockK默认不会拦截final方法,所以你写的every { project.extensions.getByType(...) }根本没生效,代码还是调用了真实Project的方法,自然找不到SourceSetContainer(因为ProjectBuilder创建的空项目本来就没有这个扩展)。
给你两种可行的修改方案:
方案一:让spyk支持拦截final方法
修改Project的spyk创建方式,加上relaxed = true,显式开启对final方法的拦截:
val project = spyk(ProjectBuilder.builder().build(), relaxed = true)
不过这种方式依赖真实Project的内部状态,可能带来不可控行为,更推荐用方案二。
方案二:完全Mock Project对象(更可控)
直接Mock Project接口,所有方法都能被存根,避免依赖真实Project的复杂逻辑:
@ExtendWith(MockKExtension::class) class CopySourceTaskTest { @MockK lateinit var customExtension: MyCustomExtension @MockK lateinit var dependencyEnabled: Property<Boolean> @MockK lateinit var sourceSetContainer: SourceSetContainer @MockK lateinit var project: Project @MockK lateinit var extensionContainer: ExtensionContainer @MockK lateinit var mainSourceSet: SourceSet @BeforeEach fun setup() { every { customExtension.enableCopyConfig } returns dependencyEnabled // 关联Project与ExtensionContainer every { project.extensions } returns extensionContainer // 存根SourceSetContainer的获取 every { extensionContainer.getByType(SourceSetContainer::class.java) } returns sourceSetContainer // 存根主源码集的获取 every { sourceSetContainer.getByName(SourceSet.MAIN_SOURCE_SET_NAME) } returns mainSourceSet // 存根copy方法,避免执行真实文件操作 every { project.copy(any()) } answers {} // 可选:存根buildDir路径 every { project.buildDir } returns File("mock-build-dir") } @Test fun testCopy() { every { dependencyEnabled.get() } returns true // 直接创建任务实例并绑定mock的project val gradleTask = CopySourceTask(customExtension).apply { this.project = project } gradleTask.run() // 验证关键调用是否执行 verify { project.copy(any()) } verify { sourceSetContainer.getByName(SourceSet.MAIN_SOURCE_SET_NAME) } } }
额外提醒:如果坚持用真实Project对象测试,除了配置spyk,还需要手动向Project注册SourceSetContainer扩展,但这种方式测试依赖更重,不如Mock方案简洁可控。
内容的提问来源于stack exchange,提问作者newbie
相关产品推荐
相关产品推荐

