-
-
Notifications
You must be signed in to change notification settings - Fork 351
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: Support Kotlin/Native in MockK #58
Comments
I wonder how that can be done technically. Does KN support any kind of instrumentation? We need at least somehow inject a check of boolean in each method that defines if the function should be intercepted or something alike. This would be compile-time instrumentation/compiler plugin, of course, the runtime is even better. I've heard Kotlin way is to write compiler plugins @olonho how is it done? |
Runtime instrumentation is indeed likely out of the question. Unless we will do something like https://en.wikipedia.org/wiki/DTrace with lightweight probes. Compile-time wise, there are options. Most obvious one is a source transformation, but it isn't really nice. Another one is to implement compiler plugins API, and do something like https://github.com/JetBrains/kotlin-native/blob/master/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/TestProcessor.kt on IR. I think plugin compiler API is most sensible option, we can try to implement an extension point which runs an additional pass, if certain plugin is available. Do you find acceptable option to implement simple IR transformer? |
Yes, IR transformer seems to be the way to go. One more possible solution is to dynamically intercept functions after code generation i.e. embedding some assembly code on the fly by using additional information that compiler provides and dispatching only needed functions. But this seems to be hard. (I believe it is similar to DTrace, just another level) Many details and design decision to be discussed if IR transformer is selected. I'll sketch just a few:
|
In general, I can admit that most of the functions of Additionally, I struggle with following problems:
|
This shall be straightforward (as in test runner above) - you can create arbitrary complex IR instead of the original function.
Without dtrace-like mechanisms, I presume it will slow down execution anyway, but smartness may come from the way of calling mocked function (smth like dynamic table of invocation targets, where either original implementations lie, or we could replace it with custom pointer when mocking). With that design, when compiled with the dynamic targets table, we could create following additional structures:
On call site of every function we call indirectly via the actual function table. Mockers can just modify this table, using elements of the original table to figure out where to call if need original implementation. This would kill performance (every call is indirect, and no inlining), but could allow rather flexible mocking without too deep integration into compiler. WDYT? |
Performance is a difficult thing to reason as many things might be optimized on CPU level e.t.c. Without performance testing and optimization I don't think we can prove anything here in discussion. Dynamic tables is kind of classics but require I believe more compiler/runtime rewrite. Better do just interception via similar approach i.e. have a table that is checked at the beginning of every function and if it is filled-in then special call to mocking framework is performed:
|
Although one thing that is better with dynamic dispatch is that it allows calling original methods, which is required by spies. See no way now to do it with interceptors without overcomplication of it. |
@olonho I have following error here:
|
We're looking on this issue. |
@olonho I use following kotlin plugin:
|
workaround for you issue could be following:
|
Thanks. Will try this evening |
As in my project DSL is extracted to specific module I need to port it first. Added
I have functions of 22 args + continatuion arg in API.kt:
Is it supported? |
Wow! After fixing this error DSL compiled in CLion and there is a possibility to run it till the moment actual implementation is needed. Optimistically tomorrow evening will port main implementation. After that only method interception is left. |
@olonho @vvlevchenko what to do with following message?
Is it possible to turn it on in CMakeLists.txt? |
To enable MPP please use |
Then how I'll get IDE support if I use gradle? |
Currently (for short period of time, we hope) it is somewhat problematic if you want IDE for Native-specific components, for common parts IDEA will work, and could be used in non-intelligent mode to edit Native-specific files as well. Longer term we will go Gralde way, so I just suggest to take that into account. |
And what about "suspend" 22 arg lambda? Should I raise ticket somewhere? |
I ported boilerplate implementation code. output Following things are marked as TODO() and required to be provided by platform/implementation:
As well to mock private properties / fun I use reflective calls. For coroutines I need runBlocking, don't know if it is supported. Additionally, my CLion is not indexing any content except I already visited, even simple serach is not working. |
Besides that I added I can say that without knowledge of IR it is quite difficult to understand what each lowering does. I tried to figure out for Autoboxing because it is small and seems even that is not so easy |
Regarding 23-parameters functions - guess this is will be fixed generally with https://youtrack.jetbrains.com/issue/KT-13764 in all Kotlin flavours. Regarding IR transformation, likely what you may need is somewhat a mix of https://github.com/JetBrains/kotlin-native/blob/master/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BridgesBuilding.kt |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. If you are sure that this issue is important and should not be marked as |
@oleksiyp Could you perhaps add |
@oleksiyp I was going through the issue and noticed two unfinished PRs (this and this). |
@ShikaSD basically interception of any call is needed. looks unrealistic on IR level. I would add NOPs at start of every function and remembered all these locations. Then when mock is created, for each function that is related to the mock change NOP to JMP instruction. It might work 😃 @olonho what do you think? |
Hmm, why do you believe it is not realistic to intercept calls on IR level? Simplest approach would be to remember all mocked functions and just modify their body IR by adding some MockK function calls in prologue and epilogue. Then you don't need to track all calls, and just focus on callee side. |
All mocked functions = all program, coz everything is covered with unit tests and everything is running in one run usually. |
It's possible to see and modify IR of the whole program, depending on place of plugin in compiler pipeline (after deserialization) and if compiler caches are used. |
I tried in these PRs and even had some success to intercept one function or alike. Rather complex, but you are right, this might be achievable. Not sure about the performance of all this thing, how to optimize it in IR the way that overhead is minimal. I was actually waiting for the promised refactoring of a compiler since the time of first PRs (tooling was not super pleasant and codebase quite complex, and I had not so powerful laptop 😃). Believe that is what is happening now, clean rewrite. I would try again, but the problem is that I became involved very tightly into Cloud topic at work and spend all my free(to learn) and work time for this purpose. |
Correct me if I am wrong, but from my perspective, for mocking we can generate anonymous implementations/wrappers of interfaces/classes user is mocking. |
@oleksiyp I have pushed results so my testbed repo. |
Maybe you are right and I am way to strict to make it way too powerful. I searched for compromises, but probably not so extensively. What can I say it may work and even cover 80% of useful cases. I checked code a bit, but no time to analyze it in details. Can say only if there is really a desire to help the project, I will invest my time as well. For testing, build pipeline and releasing. |
I am surely interested to continue with this, however it would be great to come up with list of features which should be supported, so there will be a certain list to work on. Also I have checked possible generation of implementations for |
Hard to tell all the features and actually no big need. Test cases represent features. In javascript I achieve part coverage, in Android instrumented test and JVM full coverage. Javascript version has some flaws and that prevents from making it production ready. |
Any updates here? We're looking into using kotlin multiplatform and testing library support is one of our bigger considerations. |
You can use fakes instead of mocks. |
I mostly abandoned idea myself, due to lack of time For everything final/static, mocking is quite annoying to implement as a compiler plugin, just because at the moment it would require recompiling dependencies as well. |
I have seen two libraries pop up that have provided a way to mock interfaces only in common tests which use kotlin symbol processing called MocKmp and Mockative. I have been a fan of Mockk ever since its inception and was wondering if something similar could be achieved to unlock mocking with interfaces at least with Mockk due to the limitations of Kotlin native? |
I support this topic. It is very important to be able to write mocks for Kotlin/Native. |
Yes, I have to write mocks for all platform included native too. |
I would also love to see mockk working in KMP. |
Any news on MockK for Kotlin Native? |
For multiplatform projects it would be really nice if MockK would support Kotlin/Native as well. If some missing functionality on K/N side is needed, please let us know, and we will add it.
The text was updated successfully, but these errors were encountered: