代码之家  ›  专栏  ›  技术社区  ›  ECommerce

UnitTest中MutableLiveData上的setValue和postValue

  •  23
  • ECommerce  · 技术社区  · 7 年前

    我试图在应用程序上测试一些方法,但在调用mutablelivedata时出错。postValue。以下是一个片段和错误消息:

    @Test
    public void callStartScreenRepository(){
        Observer<User> userObserver = mock(Observer.class);
        startScreenViewModel.returnUser().observeForever(userObserver);
        maennlich = new User();
        maennlich.setVorname("Christian");
        MutableLiveData<User> userTest = new MutableLiveData<>();
        userTest.postValue(maennlich);
        when(startScreenRepository.userWebService("testUser")).thenReturn(userTest);
        verify(userObserver).onChanged(maennlich);
    }
    

    http://g.co/androidstudio/not-mocked 详细信息。

    at android.os.Looper.getMainLooper(Looper.java)
    at android.arch.core.executor.DefaultTaskExecutor.postToMainThread(DefaultTaskExecutor.java:48)
    at android.arch.core.executor.AppToolkitTaskExecutor.postToMainThread(AppToolkitTaskExecutor.java:100)
    at android.arch.lifecycle.LiveData.postValue(LiveData.java:277)
    at android.arch.lifecycle.MutableLiveData.postValue(MutableLiveData.java:28)
    at com.example.cfrindt.foodtracking.ViewModels.StartScreenViewModelTest.callStartScreenRepository(StartScreenViewModelTest.java:102)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
    

    我该怎么做?

    4 回复  |  直到 7 年前
        1
  •  32
  •   Carlos Daniel    7 年前

    首先,您需要使用InstantTaskExecutorRule,这意味着:

    @Rule public InstantTaskExecutorRule instantExecutorRule = new InstantTaskExecutorRule();
    

    testCompile "android.arch.core:core-testing:$rootProject.ext.arch_version"
    

    然后,您需要使用规则覆盖rxscheduler定义JUnit规则,或者在@Before内使用RxAndroidPlugins覆盖调度程序(如果您使用的是RxJava)

    RxSchedulersOverrideRule:

    public class RxSchedulersOverrideRule implements TestRule {
        @Override public Statement apply(final Statement base, Description description) {
        return new Statement() {
            @Override public void evaluate() throws Throwable {
                RxAndroidPlugins.reset();
                RxAndroidPlugins.setMainThreadSchedulerHandler(scheduler -> Schedulers.trampoline());
    
            RxJavaPlugins.reset();
            RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline());
            RxJavaPlugins.setComputationSchedulerHandler(scheduler -> Schedulers.trampoline());
            RxJavaPlugins.setNewThreadSchedulerHandler(scheduler -> Schedulers.trampoline());
    
            base.evaluate();
    
            RxAndroidPlugins.reset();
            RxJavaPlugins.reset();
          }
        };
      }
    }
    

    @Rule public RxSchedulersOverrideRule rxSchedulersOverrideRule = new RxSchedulersOverrideRule();
    

    另一个选项是在@Before中的setup()内调用:

    RxAndroidPlugins.setInitMainThreadSchedulerHandler(schedulerCallable -> Schedulers.trampoline());
    

    RxAndroidPlugins.reset(); 
    
        2
  •  23
  •   AdamHurwitz    5 年前

    确保观察者不在主线程上

    Jeroen Mols implementation 对于JUnit 4和JUnit 5。我使用Junit 5方法,使用Kotlin、Junit 5、MockK和AssertJ库对Coinverse Open App的本地单元测试进行了测试。

    JUnit 4

    @卡洛斯·丹尼尔的 solution InstantExecutorRule 对于JUnit 4是正确的。

    示例UnitTest。千吨

    class ExampleUnitTest {
    
        @get:Rule
        val rule = InstantTaskExecutorRule()
    
        @Test
        fun `my test`() {
            val mutableLiveData = MutableLiveData<String>()
            mutableLiveData.postValue("test")
            assertEquals("test", mutableLiveData.value)
        }
    }
    

    建筑格拉德尔

    dependencies {
        testImplementation 'android.arch.core:core-testing:X.X.X'
    }
    

    InstantExecutorExtension。千吨

    import androidx.arch.core.executor.ArchTaskExecutor
    import androidx.arch.core.executor.TaskExecutor
    import org.junit.jupiter.api.extension.AfterEachCallback
    import org.junit.jupiter.api.extension.BeforeEachCallback
    import org.junit.jupiter.api.extension.ExtensionContext
    
    class InstantExecutorExtension : BeforeEachCallback, AfterEachCallback {
        override fun beforeEach(context: ExtensionContext?) {
            ArchTaskExecutor.getInstance().setDelegate(object : TaskExecutor() {
                override fun executeOnDiskIO(runnable: Runnable) = runnable.run()
                override fun postToMainThread(runnable: Runnable) = runnable.run()
                override fun isMainThread(): Boolean = true
            })
        }
    
        override fun afterEach(context: ExtensionContext?) {
            ArchTaskExecutor.getInstance().setDelegate(null)
        }
    }
    

    这个 InstantExecutorExtension 在本地单元测试类中使用注释实现。

    ...
    import androidx.paging.PagedList
    import com.google.firebase.Timestamp
    import io.mockk.*
    import org.assertj.core.api.Assertions.assertThat
    import org.junit.jupiter.api.AfterAll
    import org.junit.jupiter.api.BeforeAll
    import org.junit.jupiter.api.BeforeEach
    import org.junit.jupiter.api.Test
    import org.junit.jupiter.api.extension.ExtendWith
    
    @ExtendWith(InstantExecutorExtension::class)
    class ContentViewModelTest {
        val timestamp = getTimeframe(DAY)
    
        @BeforeAll
        fun beforeAll() {
            mockkObject(ContentRepository)
        }
    
        @BeforeEach
        fun beforeEach() {
            clearAllMocks()
        }
    
        @AfterAll
        fun afterAll() {
            unmockkAll()
        }
    
        @Test
        fun `Feed Load`() {
            val content = Content("85", 0.0, Enums.ContentType.NONE, Timestamp.now(), "",
                "", "", "", "", "", "", MAIN,
                0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0, 0.0, 0.0)
            every {
                getMainFeedList(any(), any())
            } returns MutableLiveData<Lce<ContentResult.PagedListResult>>().also { lce ->
                lce.value = Lce.Content(
                    ContentResult.PagedListResult(
                        pagedList = MutableLiveData<PagedList<Content>>().apply {
                            this.value = listOf(content).asPagedList(
                                PagedList.Config.Builder().setEnablePlaceholders(false)
                                    .setPrefetchDistance(24)
                                    .setPageSize(12)
                                    .build())
                            }, errorMessage = ""))
            }
            val contentViewModel = ContentViewModel(ContentRepository)
            contentViewModel.processEvent(ContentViewEvent.FeedLoad(MAIN, DAY, timestamp, false))
            assertThat(contentViewModel.feedViewState.getOrAwaitValue().contentList.getOrAwaitValue()[0])
                .isEqualTo(content)
            assertThat(contentViewModel.feedViewState.getOrAwaitValue().toolbar).isEqualTo(
                ToolbarState(
                        visibility = GONE,
                        titleRes = app_name,
                        isSupportActionBarEnabled = false))
            verify {
                getMainFeedList(any(), any())
            }
            confirmVerified(ContentRepository)
        }
    }
    
        3
  •  2
  •   Rainmaker    6 年前

    @Carlos Daniel 的回答对我帮助很大,但面临另一个问题 Rule 未应用于 PowerMockRunner

    @RunWith(PowerMockRunner::class)
    @PowerMockRunnerDelegate(MockitoJUnitRunner::class) //this line allows you to use the powermock runner and mockito runner
    @PrepareForTest(UnderTestClass::class)
    class UnderTestClassTest {
    @get:Rule
    var instantExecutorRule = InstantTaskExecutorRule()
    
        4
  •  2
  •   Liz    3 年前

    安德洛瓦

      implementation 'androidx.arch.core:core-testing:$version' 
    

     implementation 'android.arch.core:core-testing:$version'