Unit testing a custom gradle task using JUnit and MockK

Hello!

I am writing an unit test to test my custom gradle task.

open class CopySourceTask @Inject constructor(
    private val config: MyCustomExtension
) : DefaultTask() {

    @TaskAction
    override fun run() {
        if (!config.enableCopyConfig) {
            return
        }

        val sourceSetContainer =
            project.extensions.getByType(SourceSetContainer::class.java)

        project.copy {
            // Copy project source
            it.from(sourceSetContainer.getByName(SourceSet.MAIN_SOURCE_SET_NAME))
            it.into("${project.buildDir}/private/src")
        }
    }
}

Here’s my unit test:

@ExtendWith(MockKExtension::class)
class CopySourceTaskTest {
    @MockK
    lateinit var customExtension: MyCustomExtension

    @MockK
    lateinit var dependencyEnabled: Property<Boolean>

    @MockK
    lateinit var sourceSetContainer: SourceSetContainer

    @BeforeEach
    fun setup() {
        every { customExtension.enableCopyConfig } returns dependencyEnabled
    }

    @Test
    fun testCopy() {
        val project = spyk(ProjectBuilder.builder().build())
        every { dependencyEnabled.get() } returns true
        every { project.extensions.getByType(SourceSetContainer::class.java) } returns sourceSetContainer
        val gradleTask = project.tasks.register(
            "CopySourceTask",
            CopySourceTask::class.java,
            customExtension
        )
       
        gradleTask.get().run()

       // Verify statements here
    }
}

The unit test is erroring due to this error: org.gradle.api.UnknownDomainObjectException: Extension of type 'SourceSetContainer' does not exist. Currently registered extension types: [ExtraPropertiesExtension].

Looks like the every { project.extensions.getByType(...) statement isn’t honored and it tries to load the SourceSetContainer from the project instead of the mocked statement. I tried to split the project.extension.getByType() into two statements, project.extension that returns an extensionContainer and extensionContainer.getByType(), it resulted in the same error.

Please let me know what I’m missing.

Not knowing MockK at all, I’m just guessing from experience with other mocking frameworks, but I’d say that getProject() within your task is not returning your spy with the stubbed method calls, but the “real” project.

What do you mean by the “real” project? I initialized the project variable in the spyk() statement. And I am using that same project variable in project.tasks.register() call to create the task. I assuming that the task stores the project reference. I don’t understand why the task is returning some other project instance.

Again, I don’t know MockK specifically.
But judging from other mocking frameworks, you wrap the “real” project in spy.
You do not mock project.tasks (i.e. project.getTasks()), so the spy calls the method on the “real” project.
So the return value of project.getTasks() is the “real” TaskContainer with the “real” project that is then given to the newly created task instance.
So if your task calls anything on project it calls it on the “real” project, not on your spy.