Mock beans with Mockk failed in Spring Projects

124 Views Asked by At

This is a none Spring Boot project.

I try to use mockk() to mock a bean in the controller tests, but failed.

The PostController depends on bean PostRepository, here try to use a mockk() bean to mock the PostRepository in our test config.

@SpringJUnitConfig(classes = [PostControllerTest.TestConfig::class])
class PostControllerTest {
    companion object {
        private val log = LoggerFactory.getLogger(PostControllerTest::class.java)
    }

    @Configuration(proxyBeanMethods = false)
    @ComponentScan(basePackageClasses = [PostController::class])
    internal class TestConfig {

        @Bean
        @Primary
        fun postRepository(): PostRepository = mockk<PostRepository>()
    }

    @Autowired
    lateinit var posts: PostRepository

    lateinit var client: WebTestClient

    @BeforeEach
    fun setup() {
        log.debug("calling setup...")
        client = WebTestClient.bindToController(PostController::class)
            .configureClient()
            .build()
    }

    @AfterEach
    fun teardown() {
        clearAllMocks()
    }

    @Test
    fun `get post by id`() = runTest {
        val id = UUID.randomUUID().toString()
        coEvery { posts.findById(any()) } returns
                Post(
                    id = id,
                    title = "test title",
                    content = "content of test title"
                )
        client.get().uri("/posts/$id")
            .exchange()
            .expectStatus().isOk
            .expectBody().jsonPath("$.title").isEqualTo("test title")

        coVerify(exactly = 1) { posts.findById(any()) }
    }

}

The example project used the latest Spring 6.1.x WebFlux, Kotlin(1.9.22)/Kotlin Coroutines(1.7.3), and Java 21.

Check source codes here, https://github.com/hantsy/spring-reactive-sample/tree/master/kotlin-co

In before experience, I applied same mode which used Mockito to mock beans in Java codes, it worked.

1

There are 1 best solutions below

0
João Esperancinha On

This actually makes a lot of sense. Mockk alone isn't prepared to work with Spring. It is just a mocking library and so when you do this:

@BeforeEach
fun setup() {
    log.debug("calling setup...")
    client = WebTestClient.bindToController(PostController::class)
        .configureClient()
        .build()
}

it will try to recognize the controller, but that won't happen because at this point the Spring WebTestClient has no idea where to pick up the posts repository.

For your example what you probably need to do is something slightly different like this:

@BeforeEach
fun setup() {
    log.debug("calling setup...")
    client = WebTestClient.bindToController(PostController(posts))
        .configureClient()
        .build()
}

By handing in the instance of the controller with your mocked repository, then the WebTestClient will be able to recognize the complete controller and I'm pretty sure this will work for you. The reason being is that I have reproduced the exact same 404 problem you are having in one of my projects using a similar code like your example and after changing it, it worked perfectly.

However if you are working with mockk and want to mock spring beans, then it is probably better to use something different called Spring MockK. It works much in same way as Mockito does, but it is more adapted to work in a Kotlin style.