Spring-boot: Add support for MockK

Created on 21 Jan 2019  路  15Comments  路  Source: spring-projects/spring-boot

Spring Boot provides @MockBean and @SpyBean annotations for integration tests, which create mock/spy beans using Mockito.

Even though Mockito is a fantastic mocking library, it's been written with Java in mind, and a long time ago. MockK is becoming a quite popular alternative for code written in Kotlin, and provides (IMHO) a cleaner and safer DSL than Mockito for Kotlin.

Although it's perfectly usable in Spring Boot tests already, Spring Boot doesn't provide equivalent annotations for MockK, and tests are thus a more cumbersome to write: beans must be manually defined using a @TestConfiguration-annotated nested class, and care must be taken to reset the mocks after each test execution.

It would be nice if Spring Boot provided equivalent annotations for MockK, since Kotlin is a now a first-class citizen in Spring Boot.

The good news (or at least, I hope so)

I have created a SpringMockK library doing just that. The linked README describes the principle of the library (how it's been created), its limitations, and the differences with Mockito.

I would very much like that code to disappear from my repo and be integrated into Spring Boot.

Please tell if that would be possible, and how to proceed.

In addition to what the README contains, here is an additional useful indication: it's possible to mix Mockito tests and MockK tests in the the same project (which can be handy during a migration).

declined

Most helpful comment

English is not my mother language, and I'm not sure I understand what "I think that difference goes quite a long way to alleviate the concerns about making Boot's existing code more complex" means.

Sorry, let me have another go and see if I can explain myself more clearly.

The proposal in #9372 was to allow @MockBean and @SpyBean to be used with mocking libraries other than Mockito, with a specific focus on Spock. This would have required the existing support for @MockBean and @SpyBean to adapt to whatever's on the classpath and delegate to a Mockito-based implementation or a Spock-based implementation as appropriate. That's quite a bit of extra complexity in the implementation and some for users too. This extra implementation complexity is avoided in the approach you've taken with SpringMockK so it avoids one of the main concerns that resulted in #9372 being declined.

There's still some extra complexity for users in terms of the two different sets of similar annotations. As you've suggested, that could be avoided by splitting things up a bit more so that it's easier for users to have finer-grained control of the classpath. That has some downsides of its own though.

I'll flag this one for team attention as whether or not we add something like this is quite subjective. We'll need to try and reach a decision that everyone on the core team is happy with.

All 15 comments

Just for the context, I agree that MockK is indeed more and more the default way to mock in Kotlin, I am not super happy about Mockito Kotlin support, and I would be in favor of promoting MockK as the recommended solution if such feature would be part of Spring Boot in our documentation and tutorial.

We had a similar request in the past, but it was more focused on Spock. It was also looking to map @MockBean and @SpyBean onto Spock rather than introducing new annotations as SpringMockK does. I think that difference goes quite a long way to alleviate the concerns about making Boot's existing code more complex, but it does introduce the possibility of confusion about using @MockBean or @MockKBean. A benefit of SpringMockK being separate from Spring Boot is that @MockKBean will only be offered by your IDE if you've decided to use it and added a dependency.

I don't see why we need the equivalent of @MockBean. We use MockK extensively in our Spring Boot tests. We provide them in @Configuration classes like so:

@Bean
@Primary
fun service() = mockk<Service>(relaxed = true)  // or unrelaxed with specific behavior defined here

In the tests themselves, we inject the mocks via @Autowired and record/verify the desired behavior.


That being said I am all for promoting MockK. In my opinion, Mockito Kotlin is awful and should have been deprecated in favor of MockK long time ago.

At the very least we should definitely exclude it from all Kotlin Spring tutorials.

English is not my mother language, and I'm not sure I understand what "I think that difference goes quite a long way to alleviate the concerns about making Boot's existing code more complex" means.

Regarding the usage of separate annotations, here's what I can say:

  • MockBean and SpyBean are tightly coupled with Mockito (its answer attribute uses a Mockito type, its MockReset enum also exposes the Mockito API)
  • Having two separate sets of annotations allows the possibility of using the two frameworks in parallel
  • MockkBean and MockBean are indeed quite confusing, but I haven't found any better name to make the distinction more obvious.

In the long run, maybe it would be a good idea to extract the Mockito support and the MockK support (and any other mocking library that would be supported in the future) into additional starters, so that one could pick Mockito, or MockK, or both.

I'm currently excluding the mockito-core dependency of spring-boot-starter-test, and the IDE thus makes it clear that MockBean is a mistake because it can't find its dependencies.

@vojtapol I agree that MockkBean is not strictly needed. You can indeed use configuration classes to define the mocks (and that's what I currently do, too).

But the same could be said of MockBean: it's not strictly needed either, and you could also define the Mockito mocks using configuration classes.

Still, they're quite handy and idiomatic. Another advantage of the annotation support is that mocks are properly reset automatically between test executions.

English is not my mother language, and I'm not sure I understand what "I think that difference goes quite a long way to alleviate the concerns about making Boot's existing code more complex" means.

Sorry, let me have another go and see if I can explain myself more clearly.

The proposal in #9372 was to allow @MockBean and @SpyBean to be used with mocking libraries other than Mockito, with a specific focus on Spock. This would have required the existing support for @MockBean and @SpyBean to adapt to whatever's on the classpath and delegate to a Mockito-based implementation or a Spock-based implementation as appropriate. That's quite a bit of extra complexity in the implementation and some for users too. This extra implementation complexity is avoided in the approach you've taken with SpringMockK so it avoids one of the main concerns that resulted in #9372 being declined.

There's still some extra complexity for users in terms of the two different sets of similar annotations. As you've suggested, that could be avoided by splitting things up a bit more so that it's easier for users to have finer-grained control of the classpath. That has some downsides of its own though.

I'll flag this one for team attention as whether or not we add something like this is quite subjective. We'll need to try and reach a decision that everyone on the core team is happy with.

Thanks for the explanation @wilkinsona. Let's see what the team decides now :-)

We discussed this today, and decided that we don't think the time is right to incorporate it into Spring Boot. Thanks anyway for the offer and best of luck with the SpringMockK project. Having reviewed things again, we're, if anything, leaning towards moving @MockBean and @SpyBean out of a Mockito-specific package and making them mocking framework agnostic. That's not something that we expect to tackle in the short or even medium term.

Any chance this decision can get reconsidered, given the adoption of Kotlin over the last couple of years? It's a bit jarring to have Mockito support but not Mockk support, and it feels odd to require third-party dependencies for something as basic as mocking a collaborator bean.

From a readability standpoint, while it's possible to make do without the library, this:

    @MockBean
    private lateinit var fooService: FooService

reads a whole lot better than this:

    @TestConfiguration
    class AdditionalConfiguration {

        @Bean
        @Primary
        fun mockFooService(): FooService =  mockk()
    }

    @Autowired
    private lateinit var fooService: FooService

And while the code isn't hitting production systems, moving it under the Spring Boot umbrella would also give SecOps additional peace of mind that there are more eyes on it.

I think that if the Spring Boot team does not want to support MockK then the dependency on Mockito should be also deprecated and removed. There is no reason to give Mockito any special treatment. In fact, MockK is a much better library, with better API design and is easier to use than Mockito at least when using Kotlin.

The data that we have from start.spring.io tells us that the vast majority of Spring Boot applications are written in Java and Mockito is the most widely used mocking library for applications written in Java. While I can understand that you are frustrated, it doesn't make sense to remove support for Mockito purely because there are better alternatives for Kotlin users. It would be detrimental for Spring Boot users who choose to use Java while providing no benefit to those who choose to use Kotlin.

@emersonf We've flagged this one for team attention again. From my perspective at least, I'd still lean towards moving @MockBean and @SpyBean out of a Mockito-specific package and making them mocking framework agnostic. As above, that's not something that we expect to tackle in the short or even medium term.

I just subscribed to some spring repositories in order to try to contribute to some of spring related projects and OSS in general, mainly Java but a bit of Kotlin too as I'm working on some Android apps.
This issue caught my attention as I'm currently working on passing the Spring pro exam and diving into the Spring philosophy.

To me, choosing the implementation based on what's present in the classpath is what's spring-boot's autoconfiguration is all about. This should goes the same for the mocking framework.

We discussed this again today in our team meeting and decided that we unfortunately don't have the bandwidth to develop and maintain MockK support in Spring Boot itself. We believe this feature is still best served by the community.

Fair enough. Thanks for taking the time to discuss it and for the transparency. 馃檪

Was this page helpful?
0 / 5 - 0 ratings