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.

[Hilt] Accessing ServiceComponent bindings

See original GitHub issue

I am trying to access ServiceComponent bindings in a similar way that is described in the section “Accessing ActivityComponent bindings” from the documentation. Of course in the example, it is for activities, but I suppose I should be able to use a similar strategy for ServiceComponent.

So I am using ServiceTestRule.

But the problem is that I am not event able to start/bind to the service, I get the following error:

Main looper has queued unexecuted runnables. This might be the cause of the test failure. You might need a shadowOf(getMainLooper()).idle() call.
java.lang.Exception: Main looper has queued unexecuted runnables. This might be the cause of the test failure. You might need a shadowOf(getMainLooper()).idle() call.
	at org.robolectric.android.internal.AndroidTestEnvironment.checkStateAfterTestFailure(AndroidTestEnvironment.java:502)
	at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:581)
	at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$0(SandboxTestRunner.java:278)
	at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:89)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.util.concurrent.TimeoutException: Waited for 5 SECONDS, but service was never connected
	at androidx.test.rule.ServiceTestRule.waitOnLatch(ServiceTestRule.java:272)
	at androidx.test.rule.ServiceTestRule.bindServiceAndWait(ServiceTestRule.java:205)
	at androidx.test.rule.ServiceTestRule.bindService(ServiceTestRule.java:149)
	at com.aizo.dataowners.heatingautomation.DummyTestServiceTest.testWithBoundService(DummyTestServiceTest.kt:42)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at androidx.test.rule.ServiceTestRule$ServiceStatement.evaluate(ServiceTestRule.java:337)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:575)
	... 6 more

This is my DummyService:

@AndroidEntryPoint
internal class DummyTestService : Service() {

    override fun onBind(intent: Intent?): IBinder {
        return LocalBinder()
    }

    inner class LocalBinder : Binder() {
        // Return this instance of DummyTestService so clients can call public methods
        fun getService(): DummyTestService = this@DummyTestService
    }
}

And this is the test skeleton:

@HiltAndroidTest
@RunWith(AndroidJUnit4::class)
@UninstallModules(
    DbModule::class,
)
@MediumTest
internal class DummyTestServiceTest {

    @get:Rule
    val serviceRule = ServiceTestRule()

    @Test
    fun testWithStartedService() {
        serviceRule.startService(
            Intent(
                ApplicationProvider.getApplicationContext<Context>(),
                DummyTestService::class.java
            )
        )

        // Add your test code here.
    }

    @Test
    fun testWithBoundService() {
        val binder = serviceRule.bindService(
            Intent(
                ApplicationProvider.getApplicationContext<Context>(),
                DummyTestService::class.java
            )
        )
        val service = (binder as DummyTestService.LocalBinder).getService()
        Assert.assertNotNull(service)
    }

}

Is there some incompatibility between ServiceTestRule/Robolectric/Hilt? Let me know if you need some more info.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:5

github_iconTop GitHub Comments

1reaction
bcorsocommented, Apr 20, 2021

@ampeixoto, unfortunately I’m not familiar with the ServiceTestRule API, so I don’t have any other suggestions. I’ll close this ticket. Filing a ticket with the Androidx team or asking on stack overflow may be your best bet.

0reactions
ampeixotocommented, Apr 20, 2021

It seems it is not related with hilt… I removed any dependency to hilt for this test, (including the application=dagger.hilt.android.testing.HiltTestApplication config from the robolectric.properties file) and the error persists.

New version of the test:

@RunWith(AndroidJUnit4::class)
@MediumTest
internal class DummyTestServiceTest {

    @get:Rule
    val serviceRule = ServiceTestRule()

    @Test
    fun testWithStartedService() {
        serviceRule.startService(
            Intent(
                ApplicationProvider.getApplicationContext<Context>(),
                DummyTestService::class.java
            )
        )

        // Add your test code here.
    }

    @Test
    fun testWithBoundService() {
        val app = ApplicationProvider.getApplicationContext<Context>()
        val binder = serviceRule.bindService(
            Intent(
                app,
                DummyTestService::class.java
            )
        )
        val service = (binder as DummyTestService.LocalBinder).getService()
        Assert.assertNotNull(service)
    }

}

And when debugging the second test, I see that the app is instance of Application: image

Do you have any other suggestion I can try or should I open the ticket on the corresponding repo?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Hilt Components - Dagger
For example, a binding within an @InstallIn(ActivityComponent.class) module can only be scoped with @ActivityScoped . Hilt Component Hierarchy. Components used ...
Read more >
Dependency injection with Hilt | Android Developers
... Hilt testing guide · Hilt and Dagger annotations cheat sheet. Dagger. Dagger basics · Using Dagger in Android apps · Using Dagger...
Read more >
Components and Scoping in Hilt - Mobile Dev Notes
Scoped bindings guarantee one instance per that instance of the Container. ViewModel scoping. ViewModels can be scoped to a Fragment, a host ...
Read more >
Hilt Component classes and their scopes(Hilt Part 3) - DeHaat
Hilt component class is a class responsible for injecting the bindings into the corresponding Android classes. This is same component which we referred...
Read more >
A pragmatic guide to Hilt with Kotlin | by Filip Stanis - Medium
There are three ways to define a binding in Hilt. ... Modules are installed in a Hilt component specified using the @InstallIn annotation....
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