Skip to content

Add helper to mock module-level extension functions#586

Open
jselbo wants to merge 3 commits intomockito:mainfrom
jselbo:mock-ext-fun
Open

Add helper to mock module-level extension functions#586
jselbo wants to merge 3 commits intomockito:mainfrom
jselbo:mock-ext-fun

Conversation

@jselbo
Copy link
Contributor

@jselbo jselbo commented Mar 5, 2026

Kotlin lang ref: https://kotlinlang.org/docs/extensions.html

New syntax sugar feature: a helper to mock non-member extension functions.
Note the implementation is just mockStatic on the declaring FooKt class, so all declared extension functions would be mocked at the same time. There's no way to mock only one of the extension funs.

Also update the spotless task so you don't need to include :tests:spotlessApply

Joshua Selbo added 2 commits March 5, 2026 16:01
Top-level Kotlin extension functions compile to static methods in a
*Kt class. mockExtensionFun() simplifies creating a MockedStatic for
them by extracting the declaring class from a KFunction reference.

Includes validation that the provided function reference is actually
an extension function via KParameter.Kind.EXTENSION_RECEIVER.
The tests project is an included build, so its spotlessApply was not
invoked when running spotlessApply from the root. Register a root-level
task that depends on the included tests build's spotlessApply.
Copy link

@hick209 hick209 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this LGTM.
I just have one clarifying question.

I think we should update the kdoc and tests to make that clear for other people as well, which is the sole reason I request changes here.

*
* val ref: KFunction2<String, String, Boolean> = String::isHello
* mockExtensionFun(ref).use {
* whenever("test".isHello("sad")).thenReturn(true)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is like a static method extension as that's what those basically are for the JVM.
My question here is, does here mean that the input params, including the receiver, have to match?
In other words, does "anotherTest".isHello("sad") return false?

If so, I wonder if we could come up with an elegant way to do something like whenever(any<String>.isHello(mood)).thenReturn(true)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question. this is equivalent to doing

whenever(ref.invoke("test", "sad")).thenReturn(true)

so I tested and surprisingly yes we can use matchers in just the same way, even with the regular function invoke syntax.

whenever(any<String>().isHello(eq("sad")).thenReturn(true)

which reads kind of weird.

But you still have to follow the rule of all args being matchers. I think for that reason, we should encourage usage of this API with the explicit f.invoke because it makes the stubbing more like traditional stubbing syntax. I will update the docs and tests to reflect this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, the .invoke() syntax with matchers won't work because the compiled code for KFunction.invoke includes null checks that matchers can't satisfy unfortunately.

…d because I think it is a niche case that doesn't add much value
@jselbo jselbo requested a review from hick209 March 13, 2026 18:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants