【问题标题】:Unit test with LiveData Method getMainLooper in android.os.Looper not mocked未模拟在 android.os.Looper 中使用 LiveData 方法 getMainLooper 进行单元测试
【发布时间】:2019-02-07 13:39:18
【问题描述】:

在尝试进行单元测试时,我无法使 liveData.postValue 正常工作。我一直在谷歌中寻找解决方案,这是我现在拥有的代码。

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

我在setUpRxSchedulers 中的代码是强制性的,以避免与 Rx 出现相同的错误 (Method getMainLooper in android.os.Looper not mocked)。但是我无法解决调用 liveData.post(projectList) 时遇到的这个错误。在我检查过的所有论坛中,他们都说使用@Rule public InstantTaskExecutorRule instantExecutorRule = new InstantTaskExecutorRule(); 应该可以解决问题。但不是我的情况。

我也将视图模型放在这里以防万一:

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

}

【问题讨论】:

    标签: android unit-testing junit android-looper android-livedata


    【解决方案1】:

    InstantTaskExecutorRule 的使用实际上可以解决这个问题。

    我认为问题在于 JUnit 5 中似乎不再支持 @Rule 注释 (as Extensions are now the way to go)。代码将编译成功,但规则不会被应用。

    对此有(至少)两种解决方案:

    使用 JUnit 4

    肯定是更快,可能不是最好的,这取决于您需要多少 JUnit 5。

    这可以通过将 setup 方法的注释从 @BeforeEach 更改为 @Before 并从 JUnit 4 导入 @Test 注释来完成。 这是您的导入的外观。

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

    实现InstantTaskExecutorExtension

    如果您关心使用 JUnit 5,这会更好:)

    Here's an article 讨论如何精确实现InstantTaskExecutorExtension。 完成后,请记住使用 @ExtendWith(InstantTaskExecutorExtension::class) 注释而不是 @Rule 将其应用于您的测试类!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-08-15
      • 2020-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-23
      • 1970-01-01
      • 2017-02-03
      相关资源
      最近更新 更多