不依赖于 Android 的 JUnit 调度程序

JUnit Scheduler that doesn't depend on Android

我正在尝试使用 MVP 来增强单元测试并 运行 测试更快(因为我测试的是逻辑而不是 android 代码,所以我避免使用 RobotElectric 之类的东西)。

但我使用的是 RXAndroid,它需要 Looper 来获取 Schedulers.io()AndroidSchedulers.mainThread(),当我有时尝试 运行 时

class Phone {
    public Observable<> sendSms(String number){
        //...
    }
}

Phone.getInstance().sendSms(phoneNumber)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(phone -> {
                    mView.dismissProgress();
                    mView.startCodeView(phone);
                }, error -> {
                    mView.dismissProgress();
                    mView.showError(error);
                });

我得到:

Caused by: 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 rx.android.schedulers.AndroidSchedulers.<clinit>(AndroidSchedulers.java:27)
... 28 more

我试过了:

android {
  // ...
  testOptions { 
    unitTests.returnDefaultValues = true
  }
}

但它不会工作,因为我想 运行 完整的 JUnit 测试而不是 Roboelectric 或 Espresso 的东西。

我怎样才能完成它?有没有不会因此崩溃的调度程序?

在我们的实践中,我们尽量避免在 Presenter 中使用 AndroidSchedulers.mainThread(),因为它是 View 实现的细节。你也可以这样做。

虽然我们使用 Robolectric,但无论如何它会在我们的测试中起作用。

是的,在 junit 测试中没有 android.jar 意味着没有 Loopers。如果你使用 Dagger,你可以将一个模拟调度器注入到测试中,并将一个真正的调度器注入到源代码中。您还可以使用 Mockito 之类的东西来模拟调度程序。否则就像@Artem Zinnatullin 建议的那样,Robolectric 解决了这个问题。 Robolectric 3 使用 Android Studio 非常容易设置。

我最终添加了使用转换和 "flavour injection classes" 为此,有一个 class 将主版本用于 prod/debug 版本并在测试版本中使用另一个 class测试文件夹 Schedulers.immediate().

普通口味class:

public class Transformer {

  public static <T> Observable.Transformer<T, T> applyIoSchedulers() {
    return observable -> observable.subscribeOn(getIoScheduler())
        .observeOn(getMainScheduler());
  }

  private static Scheduler getIoScheduler() {
    return Schedulers.io();
  }

  private static Scheduler getMainScheduler() {
    return AndroidSchedulers.mainThread();
  }
}

测试风味 class:

public class Transformer {

  public static <T> Observable.Transformer<T, T> applyIoSchedulers() {
    return observable -> observable.subscribeOn(getIoScheduler())
        .observeOn(getMainScheduler());
  }

  private static Scheduler getIoScheduler() {
    return Schedulers.immediate() ;
  }

  private static Scheduler getMainScheduler() {
    return Schedulers.immediate() ;
  }
}

然后将其与转换一起使用:

mSessionRepository.login(...)
        .compose(Transformer.applyIoSchedulers())
        .subscribe(session -> { })

我也为此使用调度程序线程,但在我的测试设置和拆卸中。

@Before
public void setUp() throws Exception {
    RxAndroidPlugins.getInstance().registerSchedulersHook(new RxAndroidSchedulersHook() {
        @Override
        public Scheduler getMainThreadScheduler() {
            return Schedulers.immediate();
        }
    });
}

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

这会有帮助吗?