8

我尝试为支持架构组件生命周期事件的函数添加单元测试。为了支持生命周期事件,我@OnLifecycleEvent为我的函数添加了注释,当该事件发生时我想做一些事情。

一切都按预期工作,但我想为该函数创建一个单元测试,以检查我的函数在预期事件发生时是否运行。

 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");
    }
 }

我试图模拟 LifecycleOwner 并创建新的 LifecycleRegistery 来更改生命周期观察者的状态,但我没有这样做。

如何测试onClear()状态更改时调用的函数?

4

6 回答 6

11

您应该能够使用LifecycleRegistry

您的测试将执行如下操作:

@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
  ...
}

如果您正在测试Lifecycle.Event.ON_DESTROY那么您可能需要在此之前调用handleLifecycleEvent(Lifecycle.Event.ON_CREATE)

于 2018-10-15T06:11:00.407 回答
7

只要您正确地模拟了生命周期所有者,您就可以使用没有 Roboletric 的单元测试进行测试。

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)

当你观察一个变量时使用这个生命周期所有者,你可以使用 Mockito.verify 来查看你的回调是否被调用

于 2020-01-06T14:23:37.523 回答
3

尽管我不是一个忠实的粉丝,但我会使用Robolectric使用ActivityController来实现这一点。

鉴于在 Android 生命周期工作流中应用的观察者模式的“可观察对象”是活动、片段......应用程序上下文是必须的,我们需要以某种方式将其带入我们的测试场景。

我通过这样做达到了预期的结果

构建.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;
    }
}
于 2018-07-12T23:24:54.997 回答
1

扩展Raz's答案,在 Kotlin 中,您可以创建一个扩展函数以使其更可重用。

fun LifecycleObserver.testLifeCycleEvent(lifecycleEvent: Lifecycle.Event) {
   val mockLifeCycleOwner: LifecycleOwner = mockk()
   val lifecycleRegistry = LifecycleRegistry(mockLifeCycleOwner)
   lifecycleRegistry.addObserver(this)
   lifecycleRegistry.handleLifecycleEvent(lifecycleEvent)
}

在原始提问者的情况下,他们有一个fun bindToLifecycle(). 如果您正在做类似的事情,您也可以为此创建一个扩展函数,以便它适用于所有LifecycleObserver类型:

fun LifecycleObserver.bindToLifeCycle(lifecycle: Lifecycle) {
        lifecycle.addObserver(this);
    }

然后testLifeCycleEvent()像这样修改:

fun LifecycleObserver.testLifeCycleEvent(lifecycleEvent: Lifecycle.Event) {
   val mockLifeCycleOwner: LifecycleOwner = mockk()
   val lifecycleRegistry = LifecycleRegistry(mockLifeCycleOwner)
   this.bindLifecycle(lifecycleRegistry)
   lifecycleRegistry.handleLifecycleEvent(lifecycleEvent)
}
于 2021-03-15T15:54:54.137 回答
0

Powermock v3.8 和 RESUME 事件有一个示例。我使用这个事件来展示如何验证 livedata 值

#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)
}
于 2021-03-28T20:12:53.847 回答
0

如果你想用真正的单元测试(不是 AndroidTest)来测试它,你最好的选择是使用 Robolectric,它模拟了 Android 框架并且 Robolectric 4.0 即将问世。但是,您正在尝试测试与 Android 框架的实际交互,因此这项任务更适合真正的集成测试套件和 AndroidTest。您可以对其进行单元测试,但最可靠的测试方法是在设备上实际调用生命周期事件(Espresso 会做什么)并验证它是否被调用。

于 2018-07-05T22:21:33.327 回答