question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

BehaviorDelegate fails for suspend functions

See original GitHub issue

retrofit-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:closed
  • Created 4 years ago
  • Reactions:11
  • Comments:14 (3 by maintainers)

github_iconTop GitHub Comments

3reactions
JakeWhartoncommented, Jul 11, 2019

Yep this needs to be special-cased the same way it was inside regular Retrofit.

1reaction
vrickey123commented, Oct 26, 2019

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 HttpServiceMethod we check for requestFactory.isKotlinSuspensionFunction to determine which adapterType to use; i.e. the coroutine Response Type or a Generic Return Type.

The adapterType is then used by a private method called createCallAdapter() 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 sees java.lang.IllegalArgumentException: Could not locate call adapter for class java.lang.Object in 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 the requestFactory.isKotlinSuspensionFunction is not being hit or cannot be used by Mock Retrofit? This is where someone else’s knowledge of the library will come in handy.

Read more comments on GitHub >

github_iconTop 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 >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found