Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit test with LiveData Method getMainLooper in android.os.Looper not mocked

I cannot make liveData.postValue working in while trying to make unit test. I have been checking in google for a solution and this is the code I have now.

public class ProjectListViewModelTest {

    GetProjectList getProjectList = Mockito.mock(GetProjectList.class);
    ProjectModel.Project project = new ProjectModel.Project("testing",
            "this is a test",
            "https://logo.jpg",
            new ProjectModel.Company("cat"),
            "20150404",
            "active");
    List<ProjectModel.Project> projects = Arrays.asList(project);
    ProjectModel.ProjectList projectsList = new ProjectModel.ProjectList(projects);

    ProjectsListViewModel projectsListViewModel;

    private PublishSubject<ProjectModel.ProjectList> projectsListPublishSubject = PublishSubject.create();

    @Rule public InstantTaskExecutorRule instantExecutorRule = new InstantTaskExecutorRule();

    @BeforeClass
    public static void setUpRxSchedulers() {
        Scheduler immediate = new Scheduler() {
            @Override
            public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
                return super.scheduleDirect(run, 0, unit);
            }

            @Override
            public Scheduler.Worker createWorker() {
                return new ExecutorScheduler.ExecutorWorker(Runnable::run);
            }
        };

        RxJavaPlugins.setInitIoSchedulerHandler(scheduler -> immediate);
        RxJavaPlugins.setInitComputationSchedulerHandler(scheduler -> immediate);
        RxJavaPlugins.setInitNewThreadSchedulerHandler(scheduler -> immediate);
        RxJavaPlugins.setInitSingleSchedulerHandler(scheduler -> immediate);
        RxAndroidPlugins.setInitMainThreadSchedulerHandler(scheduler -> immediate);
    }

    @Before
    @Throws(exceptionClasses = Exception.class)
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        projectsListViewModel = new ProjectsListViewModel(getProjectList);
        when(getProjectList.execute()).thenReturn(projectsListPublishSubject.take(1).singleOrError());
    }

    @Test
    public void testExecuteGetProjectsListSuccess() {
        LiveData<List<ProjectModel.MapProject>> liveData = projectsListViewModel.getLiveData();
        ProjectModel.MapProject expectedResult = new ProjectModel.MapProject(
                "testing", "this is a test", "https://logo.jpg",
                "cat", "2015-04-04", "active");
        projectsListViewModel.getProjects();
        projectsListPublishSubject.onNext(projectsList);
        Assert.assertEquals(expectedResult, liveData.getValue().get(0));
    }

    @After
    public void tearDownClass(){
        RxAndroidPlugins.reset();
    }

The code that I have in setUpRxSchedulers is mandatory in order to avoid the same error (Method getMainLooper in android.os.Looper not mocked) with Rx. But I cannot solve this error that I get when calling liveData.post(projectList). In all the forums that I checked they say that with @Rule public InstantTaskExecutorRule instantExecutorRule = new InstantTaskExecutorRule(); the problem should be solved. But is not my case.

I put here the viewmodel as well in case it can help:

public class ProjectsListViewModel extends ViewModel {

    GetProjectList getProjectList;
    MutableLiveData<List<ProjectModel.MapProject>> liveData = new MutableLiveData<>();

    public ProjectsListViewModel(GetProjectList getProjectList){
        this.getProjectList = getProjectList;
    }

    public LiveData<List<ProjectModel.MapProject>> getLiveData(){
        return liveData;
    }

    public void getProjects(){
        getProjectList.execute()
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
                .map(ProjectModel.ProjectList::getProjects)
                .toObservable().flatMapIterable(projects -> projects)
                .map(project -> project.convertToMapProject()).toList()
        .subscribe(projectsList ->
            liveData.setValue(projectsList));
    }

}
like image 232
Iban Arriola Avatar asked Nov 06 '22 21:11

Iban Arriola


1 Answers

The usage of InstantTaskExecutorRule will actually solve this.

I think the issue is that in JUnit 5 the @Rule annotation doesn't seem to be supported anymore (as Extensions are now the way to go). The code will compile successfully, but the rule just won't be applied.

There are (at least) two solutions to this:

Use JUnit 4

Is definitely the quicker, may not be the best depending on how much you need JUnit 5.

This can be done just by changing the annotation of your setup method from @BeforeEach to @Before and by importing the @Test annotation from JUnit 4. Here's how your imports should look like.

import org.junit.Before
import org.junit.Rule
import org.junit.Test

Implement a InstantTaskExecutorExtension

This is better if you care about using JUnit 5 :)

Here's an article that talks about how to implement precisely InstantTaskExecutorExtension. Once that's done remember to apply it to your test class using the @ExtendWith(InstantTaskExecutorExtension::class) annotation instead of @Rule!

like image 94
Marino Avatar answered Nov 12 '22 19:11

Marino