BehaviorDelegate fails for suspend functions
See original GitHub issueretrofit-mock’s BehaviorDelegate throws an exception when attempting to return for a suspend function.
For example
interface SomeApi {
@GET("foo")
suspend fun getFoo(): Foo
}
will throw when attempting to use:
val delegate: BehaviorDelegate<SomeApi> = mockRetrofit.create(SomeApi::class.java)
val foo: Foo = delegate.returningResponse(Foo()).getFoo()
I’d submit a failing test for this, but it would involve me pulling in Kotlin into the retrofit-mock module which I’m not sure is desirable?
Exception thrown looks like:
java.lang.IllegalArgumentException: Could not locate call adapter for class java.lang.Object.
Tried:
* retrofit2.CompletableFutureCallAdapterFactory
* retrofit2.DefaultCallAdapterFactory
at retrofit2.Retrofit.nextCallAdapter(Retrofit.java:241)
at retrofit2.Retrofit.callAdapter(Retrofit.java:205)
at retrofit2.mock.BehaviorDelegate$1.invoke(BehaviorDelegate.java:64)
at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
at $Proxy0.getGames(Unknown Source)
at au.com.gridstone.debugdrawer.sampleapp.MockGamesApi.getGames(MockGamesApi.kt:17)
at au.com.gridstone.debugdrawer.sampleapp.GamesViewModel$refresh$1.invokeSuspend(GamesViewModel.kt:43)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7319)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:934)
Issue Analytics
- State:
- Created 4 years ago
- Reactions:11
- Comments:14 (3 by maintainers)
Top Results From Across the Web
kotlin - Suspend function reference as parameter of let gets error
You can only call suspend functions from a coroutine or another suspend function. And let does not take a suspend function as a...
Read more >Composing suspending functions | Kotlin
This problem does not happen with structured concurrency, as shown in the section below. Structured concurrency with async. Let us take the ...
Read more >Retrofit passing in suspend function - CommonsWare
The problem I ran into was calling this function. I get the error message. Type mismatch. Required: suspend () → Response<TypeVariable(T)>
Read more >Suspend what you're doing: Retrofit has now Coroutines ...
Retrofit 2.6.0 has been released with support for suspend functions. This allows you to express the asynchrony of HTTP requests in an ...
Read more >Differences of Suspend Functions with Retrofit on Exceptions
For the ObjectService, when the API responses the error code of 400, suspend fun getString(): String? throws the exception. val retrofit = Retrofit.Builder()...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found

Yep this needs to be special-cased the same way it was inside regular Retrofit.
I took a look at the logs and I believe I have found the root cause and special casing that Jake mentioned earlier.
It looks like in
HttpServiceMethodwe check forrequestFactory.isKotlinSuspensionFunctionto determine whichadapterTypeto use; i.e. the coroutine Response Type or a Generic Return Type.The
adapterTypeis then used by a private method calledcreateCallAdapter()to return an adapter appropriate for a couroutine Response or a Generic Response.https://github.com/square/retrofit/blob/master/retrofit/src/main/java/retrofit2/HttpServiceMethod.java#L43-L66
In the Mock Retrofit
BehaviorDelegate, we naively use the Generic Response Type to create a Call Adapter which is why everyone seesjava.lang.IllegalArgumentException: Could not locate call adapter for class java.lang.Objectin their logs.https://github.com/square/retrofit/blob/master/retrofit-mock/src/main/java/retrofit2/mock/BehaviorDelegate.java#L57-L61
Rather than expose the private method for
createCallAdapter, can we think of a reason why therequestFactory.isKotlinSuspensionFunctionis not being hit or cannot be used by Mock Retrofit? This is where someone else’s knowledge of the library will come in handy.