I tried to add a unit test for my function which supports architecture components lifecycle event. To support lifecycle event, I added the @OnLifecycleEvent
annotation for my function which I want to do something when that event occurred.
Everything is working as expected but I want to create a unit test for that function to check my function running when the intended event occurred.
public class CarServiceProvider implements LifecycleObserver {
public void bindToLifeCycle(LifecycleOwner lifecycleOwner) {
lifecycleOwner.getLifecycle().addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onClear() {
Log.i("CarServiceProvider", "onClear called");
}
}
I tried to mock LifecycleOwner and create new LifecycleRegistery to change the state of lifecycle observer but I didn't do.
How can I test my onClear()
function called when state changed?
You should be able to use the LifecycleRegistry
Your test would do something like below:
@Test
public void testSomething() {
LifecycleRegistry lifecycle = new LifecycleRegistry(mock(LifecycleOwner.class));
// Instantiate your class to test
CarServiceProvider carServiceProvider = new CarServiceProvider();
carServiceProvider.bindToLifeCycle(lifecycle);
// Set lifecycle state
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
// Verify that the ON_STOP event was handled, with an assert or similar check
...
}
If you are testing Lifecycle.Event.ON_DESTROY then you probably need to call handleLifecycleEvent(Lifecycle.Event.ON_CREATE) prior to this.
You can test using Unit tests without Roboletric as long as you mock the lifecycle owner properly.
val lifecycleOwner: LifecycleOwner = Mockito.mock(LifecycleOwner::class.java)
val lifecycle = LifecycleRegistry(Mockito.mock(LifecycleOwner::class.java))
lifecycle.markState(Lifecycle.State.RESUMED)
Mockito.`when`(lifecycleOwner.lifecycle).thenReturn(lifecycle)
Use this lifecycleOwner when you observe a variable and you can use Mockito.verify to see if your callback has been called
Despite of I am not a big fan, I would go with Robolectric making use of ActivityController to achieve this.
Given the fact that the "Observables" of the Observer pattern applied in Android Lifecycle workflow are Activities, Fragments... An application context is a must and we need somehow bring it to our test scenario.
I achieved the expected result by doing this
build.gradle
testCompile "org.robolectric:robolectric:3.5.1"
CarServiceProviderTest.java
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class)
public class CarServiceProviderTest {
@Test
public void shouldFireOnClear(){
//Grab the Activity controller
ActivityController controller = Robolectric.buildActivity(JustTestActivity.class).create().start();
AppCompatActivity activity = (AppCompatActivity) controller.get();
//Instanciate our Observer
CarServiceProvider carServiceProvider = new CarServiceProvider();
carServiceProvider.bindToLifeCycle(activity);
//Fire the expected event
controller.stop();
//Assert
Assert.assertTrue(carServiceProvider.wasFired);
}
}
CarServiceProvider.java
public class CarServiceProvider implements LifecycleObserver {
public boolean wasFired;
public void bindToLifeCycle(LifecycleOwner lifecycleOwner) {
lifecycleOwner.getLifecycle().addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onClear() {
wasFired = true;
}
}
Expanding on Raz's
answer, in Kotlin you can make an extension function to make this more reusable.
fun LifecycleObserver.testLifeCycleEvent(lifecycleEvent: Lifecycle.Event) {
val mockLifeCycleOwner: LifecycleOwner = mockk()
val lifecycleRegistry = LifecycleRegistry(mockLifeCycleOwner)
lifecycleRegistry.addObserver(this)
lifecycleRegistry.handleLifecycleEvent(lifecycleEvent)
}
In the original questioner's case, they have a fun bindToLifecycle()
. If you are doing something like that, you can just make an extension function for that as well so it applies to all LifecycleObserver
types:
fun LifecycleObserver.bindToLifeCycle(lifecycle: Lifecycle) {
lifecycle.addObserver(this);
}
and then modify testLifeCycleEvent()
like this:
fun LifecycleObserver.testLifeCycleEvent(lifecycleEvent: Lifecycle.Event) {
val mockLifeCycleOwner: LifecycleOwner = mockk()
val lifecycleRegistry = LifecycleRegistry(mockLifeCycleOwner)
this.bindLifecycle(lifecycleRegistry)
lifecycleRegistry.handleLifecycleEvent(lifecycleEvent)
}
If you want to test it with a real Unit test (not AndroidTest), your best bet is to use Robolectric, it mocks the Android framework and Robolectric 4.0 is coming out. However you're trying to test the actual interaction with the Android Framework, so that's a task better suited for a true integration testing suite and an AndroidTest. You can unit test it but the most sure way to test it would be to actually invoke the lifecycle event on a device (What Espresso would do) and verify it's called.
There is a sample for Powermock v3.8 and RESUME event. I use this event to show how to verify livedata value
#build.gradle
dependencies {
...
testImplementation 'org.robolectric:robolectric:3.8'
testImplementation "org.powermock:powermock-module-junit4:2.0.2"
testImplementation "org.powermock:powermock-module-junit4-rule:2.0.2"
testImplementation "org.powermock:powermock-api-mockito2:2.0.2"
testImplementation "org.powermock:powermock-classloading-xstream:1.6.4"
...
}
#CustomViewModel.kt
class CustomViewModel() : ViewModel(), LifecycleObserver {
private val _outputData = MutableLiveData<Boolean>()
val outputData: LiveData<Boolean> = _outputData
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() {
//TODO action
outputData.value = true
}
}
#CustomViewModelTest.kt
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.powermock.core.classloader.annotations.PowerMockIgnore
import org.powermock.modules.junit4.PowerMockRunner
import org.powermock.modules.junit4.PowerMockRunnerDelegate
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(PowerMockRunner::class)
@PowerMockRunnerDelegate(RobolectricTestRunner::class)
@PowerMockIgnore("org.mockito.*", "org.robolectric.*", "androidx.*")
@Config(manifest = Config.NONE)
class CustomViewModelTest {
@Mock
lateinit var observer: Observer<in Boolean>
@Before
fun beforeTest() {
MockitoAnnotations.initMocks(this)
}
@Test
@Config(sdk = [Build.VERSION_CODES.O])
fun `do an action on resume event`() {
val vm = spy(CustomViewModel())
vm.outputData.observeForever(observer)
vm.testLifecycleEvent(Lifecycle.Event.ON_RESUME)
verify(vm).onResume()
verify(observer).onChange(true)
}
}
fun LifecycleObserver.testLifecycleEvent(lifecycleEvent: Lifecycle.Event) {
val lifecycleOwner = Mockito.mock(LifecycleOwner::class.java)
val lifecycleRegistry = LifecycleRegistry(lifecycleOwner)
this.bindToLifecycle(lifecycleRegistry)
lifecycleRegistry.handleLifecycleEvent(lifecycleEvent)
}
fun LifecycleObserver.bindToLifecycle(lifecycle: Lifecycle) {
lifecycle.addObserver(this)
}
You can use the TestLifecycleOwner from the
androidx.lifecycle:lifecycle-runtime-testing
dependency
https://developer.android.com/reference/kotlin/androidx/lifecycle/testing/TestLifecycleOwner
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.