10

I am trying to write UI test for my Fragments with Espresso and MockWebServer. I am using OkHttp Idling Resource to tell Espresso to wait for the API calls to complete.

But somehow, sometimes my tests are working but sometimes not, because Espresso does its job before service request. So my recyclerView item click test fail.

I can not figure out why espresso does not wait for the API calls? Can anyone help?

Here is my test class

@MediumTest
@HiltAndroidTest
@UninstallModules(PersistenceModule::class, NetworkModule::class)
class MainFragmentTest {

    @get:Rule
    var hiltRule = HiltAndroidRule(this)

    private val mockWebServer = MockWebServer()

    @Inject
    lateinit var okHttp3IdlingResource: OkHttp3IdlingResource

    @Before
    fun setUp() {
        hiltRule.inject()
        // Prepare Mock Web Server
        mockWebServer.start(8080)
        IdlingRegistry.getInstance().register(okHttp3IdlingResource)
    }

    @Test
    fun mainFragmentTest(){

        // Prepare Mock Web Server
        mockWebServer.dispatcher = object : Dispatcher() {
            override fun dispatch(request: RecordedRequest): MockResponse {
                return MockResponse()
                    .setResponseCode(200)
                    .setBody(MockResponseFileReader("post_success.json").content)
            }
        }

        val navController = mockk<NavController>(relaxed = true)

        launchFragmentInHiltContainer<MainFragment> {
            Navigation.setViewNavController(requireView(), navController)
        }

        onView(withId(R.id.rv_posts)).perform(
            RecyclerViewActions.actionOnItemAtPosition<PostViewHolder>(
                0,
                click()
            )
        )


        verify { navController.navigate(MainFragmentDirections.actionMainFragmentToDetailFragment().setPost(any())) }
    }

    @After
    fun tearDown() {
        mockWebServer.close()
    }

}

Here is my TestNetworkModule that holds network dependencies

@Module
@InstallIn(SingletonComponent::class)
object TestNetworkModule {

    /**
     * Provides [OkHttpClient] instance
     */
    @Provides
    @Singleton
    fun provideOkHttpClient() : OkHttpClient {
        return OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(30, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .build()
    }

    /**
     * Provides [OkHttp3IdlingResource] instance
     */
    @Provides
    @Singleton
    fun provideIdlingResource(okHttpClient: OkHttpClient) : OkHttp3IdlingResource {
        return OkHttp3IdlingResource.create(
            "okhttp",
            okHttpClient
        )
    }

    /**
     * Provides [Retrofit] instance
     */
    @Provides
    @Singleton
    fun provideRetrofit(okHttpClient: OkHttpClient) : Retrofit {
        val contentType = "application/json".toMediaType()
        return Retrofit.Builder()
            .baseUrl("http://127.0.0.1:8080")
            .client(okHttpClient)
            .addConverterFactory(Json.asConverterFactory(contentType))
            .build()
    }

    /**
     * Provides [ApiService] instance
     */
    @Provides
    @Singleton
    fun provideApiService(retrofit: Retrofit): ApiService {
        return retrofit.create(ApiService::class.java)
    }

}

I do not know what I am missing so any help will be appreciated

Current Versions;

androidTestImplementation "com.squareup.okhttp3:mockwebserver:4.6.0"
androidTestImplementation 'com.jakewharton.espresso:okhttp3-idling-resource:1.0.0'
ysfcyln
  • 2,857
  • 6
  • 34
  • 61
  • Curious, how does your `OkHttp3IdlingResource` class look like? – Aaron Mar 28 '21 at 20:06
  • It comes from `com.jakewharton.espresso:okhttp3-idling-resource` library @Aaron I provide it in my `TestNetworkModule` – ysfcyln Mar 28 '21 at 20:10
  • 1
    My best guess is that your idling resource isn't being registered correctly. I'd confirm this by adding a delay to your network responses and seeing that it always fails (since it would hopefully remove the possibility for a race condition). I'd then try setting up your idling resources as a test rule instead of how you have: https://medium.com/insiden26/okhttp-idling-resource-for-espresso-462ef2417049 – agoff Mar 29 '21 at 14:46
  • @agoff so you mean, should I use test rule for register `OkHttp Idling Resource`. If so how can I inject `OkHttpClient` to rule? – ysfcyln Mar 29 '21 at 16:26
  • I'm not 100% sure you'd need to, but I think you could set up your `OkHttpIdlingResourceRule` to take in an `OkHttpClient` and use that instead of `OkHttpProvider.instance` when declaring the resource. – agoff Mar 29 '21 at 16:35
  • Yes, but this way I do not know how pass the `OkHttp` instance to rule that I provided in `TestNetworkModule` @agoff – ysfcyln Mar 29 '21 at 17:34
  • Is it failing when you run a single test or a set of tests? – Anatolii Apr 04 '21 at 21:58
  • Both @Anatolii I tried with single and multiple – ysfcyln Apr 05 '21 at 17:03
  • 2
    @ysfcyln it would be nice if you could create a sample project on github for this. I cannot see the problem so far. – Anatolii Apr 06 '21 at 10:50
  • It would be very nice but unfortunately it is not possible for now :( @Anatolii – ysfcyln Apr 06 '21 at 20:33
  • @ysfcyln btw, you never unregister your `okHttp3IdlingResource`, at least it's not shown anywhere in your code. – Anatolii Apr 07 '21 at 12:01
  • @ysfcyln, did you get the solution for the above one, as i am also facing the similar issue, could you please assist me – Reprator Jun 11 '21 at 02:21
  • @Reprator unfortunately, I couldn't find a solution so instead of `MockWebServer` I used `fake repository` approach – ysfcyln Jun 11 '21 at 11:42
  • @ysfcyln, i had to use idling resource alongwith that, now working for me, – Reprator Jun 13 '21 at 02:10
  • I suppose Espresso sometimes moves out of the idling state while data is still being processed. So you have a race condition where your tests will assert before the data actually has been shown on screen. – bompf Jul 27 '21 at 08:34
  • Do you have EarlyEntryPoint implemented in your dagger setup? – Levon Petrosyan Feb 20 '23 at 04:46

0 Answers0