Hi everyone! I'm excited to announce the release o...
# feed
d
Hi everyone! I'm excited to announce the release of Mokkery, a mocking library designed for Kotlin Multiplatform and driven by the compiler plugin. With Mokkery, mocking and spying on various types for any Kotlin target has never been easier! Currently, the library supports mocking for types that are fully overridable, such as interfaces, function types, and open/abstract classes with a no-args constructor. It is compatible with Kotlin
1.8.20
,
1.8.21
,
1.8.22
and
1.9.0
. It supports JVM, JS, and all 3 tiers of Kotlin Native targets. If you have any questions, need assistance, or simply want to share your experiences, feel free to reach out. K
K 12
p
hi Damian, what are the practical differences compared to MockK? would be worth mentioning it in the README. I see it’s compiler plugin-driven, but it looks like a buzzword, I don’t really know what it means for me as an end user 🙂
d
As far as I know Mockk supports JVM target and partially JS target. This library works for JVM, JS and all 3 tiers of Kotlin native targets. Thats what I mean by "designed for Kotlin multiplatform".
💡 1
👆 1
k
Does this library support mocking classes? Because currently i use MocKMP which only supports mocking interfaces because they say mocking anything but interfaces is bad code design.
d
You can mock classes that have all methods abstract/open and there is a no-arg constructor. Currently mocking final classes is not supported.
K 2
Also, you should prefer interfaces as it is a better practice.
1
k
what's better practice on single implementation interfaces?
d
In principle, if you do unit tests there is always more than one implementation. Even if there is one implementation in production code, you need another one for testing.
1
Does it answer your question? Because I am not sure if I interpreted it correctly.
j
Personally I don't like the boilerplate of interfaces for single implementation classes myself. I have come into a use case for mocking a KMP class that doesn't have an interface and I don't want to convert it to an interface just to exercise tests. I ported the class from an upstream JVM-only library, so I want to keep the code inline with that implementation. They're mocking with Mockito in tests, so I've just kept that particular test suite JVM-only for now. I'll check out your library to see if it would work in this case.
d
Yea I know what you mean. For now it's challenging or even impossible to mock final classes on kotlin native, so we have to deal with it somehow and using interfaces seems like a good idea.
j
For final classes, could something similar to the all-open compiler plugin be used to make the class open during test compilation only?
d
I think you might try to apply it for main and see if it works.
But I am not sure what all open plugin actually does. Currently mokkery plugin is applied only for test sources and at this point classes from main cannot be transformed.
r
What do you see as the main differences between this and MocKMP referenced above, which is also mpp and uses a compiler plugin?
j
I haven't used all-open myself, but it looks like you can specify annotations that indicate classes that should be open. I wonder if it could be used in a way where you define an annotation like
@OpenForMock
that indicates the class should be final during regular compilation, but open for test compilation specifically. This could work in conjunction with your mock compiler plugin applied to tests.
d
@rocketraman MockKMP requires some boilerplate and it does not support classes at all. Also it uses code generation, so IDE autocomplete sometimes does not work. So in general mokkery requires less code and it's more friendly. Mokkery is more mockk-like.
👀 1
👍 3
@Jeff Lockhart sounds good. Please text me if you try it.
👍🏼 1
r
Just for the record, I've only used mockk and resisted trying MocKMP because of how different it seemed to be. Its great to see this option and I'll definitely try it out in the future!
K 2
1
s
Some important unit tests for my product Ashampoo Photos run JVM-only because I used Mockito so far. Until now native code behaved exactly like on the JVM, but switching to full native unit tests for everything will give me peace of mind. Thank you for making this, I will try it out!
❤️ 1
K 1
d
@Jeff Lockhart I tried your idea and it seems to work. It is possible to mock final class when all-open plugin annotation is applied to this class. Thank you for the idea! I'm not sure if i have to add this functionality to my plugin. Quick note about this interaction with all-open plugin in the doc should be enough.
👍 1
👍🏼 1
s
@Damian Baczyński Agreed. All-open is not what everyone may want, so it should be optional. I consider mocking anything else than interfaces a bad practice and would not like to see workarounds for that in my project.
If people convince you to I include it, you can make separate artifact that bundles the original, all-open and any glue code.
k
what's the benefit of mock vs fake implementation?
s
I do fake implementations / dummy objects by hand, if I really need them. In Ashampoo Photos I plug in a fake photo source where I need it.
👍 1
r
I tried your idea and it seems to work. It is possible to mock final class when all-open plugin annotation is applied to this class. Thank you for the idea! I'm not sure if i have to add this functionality to my plugin. Quick note about this interaction with all-open plugin in the doc should be enough. (edited)
Doing all-open in this way would make it open for released code as well wouldn't it? Ideally the open would apply only during tests.
s
Overwriting the functionality of a real object… and if you make a mistake the real object answers instead of the fake… and you may not notice it. In the past I saw some mocking of real classes and it was always a mess with a lot of pitfalls. I don’t go there. 🤷‍♂️
d
@rocketraman Probably yes, but it should not be a problem for e.g. mobile applications. Library authors tend to use interfaces more and this workaround should not be required.
1
👍 1
s
Also it’s not a good idea if the code behaves a lot different under unit test than in production. You want to absolutely minimize that. If the test runs green because the code can do something it can’t in production the test is worthless.
👍 2
d
I'm not sure if performing
all-open
behavior for main source set only before test set is easy to do. For me
all-open
workaround should be enough. Maybe in the future I will explore this, but of course as an optional feature 😄.
👍 1
j
I'd consider the case I've run into less common. I didn't write the original tests that use the mocks, but I've found them to be effective. The code being mocked (part of a database API) is part of a different library than the code being tested (a Flow extension on the database's callback API). Even now that I have merged both APIs into my own KMP project (still separate module artifacts), I wouldn't change the API from classes to interfaces just to facilitate this specific test. The fact that mocking libraries like Mockito and MockK support mocking final classes, at least on the JVM, indicates there are valid use cases where this comes in handy in certain situations. I don't think it should require forcing bad practice where a mock should otherwise not be used of course.
z
I use MockK. Why might I want to switch?
r
@Zachary Siegel Multiplatform