Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setValue and postValue on MutableLiveData in UnitTest

Tags:

I try to test some methods on my Application but I get an error when calling mutablelivedata.postValue. Here is a snippet and the error message:

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

java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked. See http://g.co/androidstudio/not-mocked for details.

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) 

I receive this error no matter if I am using setValue() or postValue(). How can I make it work?

like image 371
ECommerce Avatar asked Aug 31 '17 18:08

ECommerce


People also ask

What is difference between setValue and postValue?

The setValue is a class that holds observable data. Live data is lifecycle-aware. postValue() is not lifecycle aware. The value will be updated whenever the main thread runs.

What is postValue?

postValue(): Posts a task to a main thread to set the given value. If you called this method multiple times before a main thread executed a posted task, only the last value would be dispatched.

What is difference between LiveData and MutableLiveData?

By using LiveData we can only observe the data and cannot set the data. MutableLiveData is mutable and is a subclass of LiveData. In MutableLiveData we can observe and set the values using postValue() and setValue() methods (the former being thread-safe) so that we can dispatch values to any live or active observers.

What is MutableLiveData?

MutableLiveData. MutableLiveData is just a class that extends the LiveData type class. MutableLiveData is commonly used since it provides the postValue() , setValue() methods publicly, something that LiveData class doesn't provide.


1 Answers

First thing is that you need to work with the InstantTaskExecutorRule, that means:

@Rule public InstantTaskExecutorRule instantExecutorRule = new InstantTaskExecutorRule(); 

This will allow to work with MutableLiveData instantly. Remember to include in your app's build.gradle the import:

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

Then, you need to either define a JUnit Rule with a rule overriding RxSchedulers or within the @Before override the Schedulers as well using RxAndroidPlugins (if you are using 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();       }     };   } } 

And calling it in the test class:

@Rule public RxSchedulersOverrideRule rxSchedulersOverrideRule = new RxSchedulersOverrideRule(); 

The other option is calling within setup() in @Before:

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

After using this you need to reset in @After's tearDownClass():

RxAndroidPlugins.reset();  
like image 188
Carlos Daniel Avatar answered Oct 11 '22 21:10

Carlos Daniel