mockito / mockito-kotlin

Using Mockito with Kotlin
MIT License
3.09k stars 198 forks source link

Add `doReturn().on { method() }` helper to KStubbing #481

Closed thecodewarrior closed 1 year ago

thecodewarrior commented 1 year ago

This implements my proposed solution to #453, which provides a cleaner way to use the doReturn().when(mock).methodCall() mocking syntax. Mockito-kotlin provides a .whenever(mock) extension, since when is a reserved keyword in Kotlin, however inside a KStubbing you still need to specify the instance, which gets messy and verbose.

I added one extension function to KStubbing: Stubber.on(methodCall).

// before
val myMock = mock<MyClass> {
    doReturn("first").whenever(it).myMethod()
}
// after
val myMock = mock<MyClass> {
    doReturn("first").on { myMethod() }
}

I went with doReturn(...).on { ... } instead of doReturn(...) on { ... } because of the difference in how the results are chained:

val myMock = mock<MyClass> {
    // `on {} doReturn X doThrow Y`   - uniformly uses spaces
    // `doReturn(X).doThrow(Y).on {}` - uniformly uses dots
    // `doReturn(X).doThrow(Y) on {}` - inconsitent dots/spaces

    on { myMethod() } doReturn "value" doThrow IllegalStateException()
    doReturn("first").doReturn("second").doThrow(IllegalStateException()).on { myMethod() }
    doReturn("first").doReturn("second").doThrow(IllegalStateException()) on { myMethod() }
}

This method doesn't provide type safety, but it's a simple and effective solution. We could create an entire custom KStubber<T> class that does type safety, however, while that will provide type safety, it won't provide type inference, since you do the .on { myMethod() } at the end.

We could provide type safety and type inference given a different syntax, but it's awkward.

val myMock = mock<MyClass> {
    on({ myMethod() }) {
        doReturn("first")
        doReturn("second")
        doThrow(IllegalStateException())
    }
}

There are probably other possible syntaxes, but this is the best one I came up with.