假设我有一个公开两个方法的API,每个方法都返回一个可观察的
import org.assertj.core.util.VisibleForTesting;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.Scheduler;
class SomeApiClass {
private static final String[] doOnSubscribeThread = new String[1];
static Observable<Integer> immediatelyDoWork() {
return Observable.just(1, 2)
.doOnSubscribe(ignore -> doOnSubscribeThread[0] = Thread.currentThread().getName())
.flatMap(ignore -> doWork());
}
static Observable<Integer> periodicallyDoWork() {
// interval is using default computation scheduler
return Observable.interval(1, TimeUnit.SECONDS)
.doOnSubscribe(ignore -> doOnSubscribeThread[0] = Thread.currentThread().getName())
.flatMap(ignore -> doWork());
}
@VisibleForTesting
static String getSubscribedOnThread() {
return doOnSubscribeThread[0];
}
private static Observable<Integer> doWork() {
return Observable.create(emitter -> {
Random random = new Random();
emitter.onNext(random.nextInt());
emitter.onComplete();
});
}
大多数API只会让调用应用程序设置subscribeOn
线程(假设这些测试是我的应用程序):
import org.junit.Test;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.observers.TestObserver;
import io.reactivex.schedulers.Schedulers;
import static com.google.common.truth.Truth.assertThat;
public class ExampleTest {
@Test
public void canSetSubscribeOnThread() {
Observable<Integer> coloObservable = SomeApiClass.immediatelyDoWork()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
TestObserver<Integer> testObserver = coloObservable.test();
testObserver.awaitCount(2); // wait for a few emissions
assertThat(SomeApiClass.getSubscribedOnThread()).contains("RxNewThreadScheduler");
}
@Test
public void canSetSubscribeOnThreadIfApiUsesInterval() {
Observable<Integer> coloObservable = SomeApiClass.periodicallyDoWork()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
TestObserver<Integer> testObserver = coloObservable.test();
testObserver.awaitCount(2); // wait for a few emissions
assertThat(SomeApiClass.getSubscribedOnThread()).contains("RxNewThreadScheduler");
}
}
在immediate
示例中的 IIUC,所有订阅副作用(包括just()
)都将在新线程上发生。 Karnok explains well here。
但是在periodic
示例中,interval
将使用默认(计算)调度程序。在这种情况下,大多数API会做什么?他们是否让调用者为所有订阅副作用 interval
本身设置subscriptionOn线程?在上面的periodic
测试中,我们仍然可以为interval
以外的所有内容设置subscribeOn线程。还是他们也添加了一个参数来设置subscribeOn:
/**
* Works like {@link #periodicallyDoWork()} but allows caller to set subscribeOnSchedueler
*/
static Observable<Integer> periodicallyDoWork(Scheduler subscribeOnScheduler) {
return Observable.interval(1, TimeUnit.SECONDS, subscribeOnScheduler)
.doOnSubscribe(ignore -> doOnSubscribeThread[0] = Thread.currentThread().getName())
.flatMap(ignore -> doWork());
}
然后允许调用者忽略subscribeOn()方法:
@Test
public void canSetSubscribeOnThreadIfApiUsesInterval() {
Observable<Integer> coloObservable = SomeApiClass.periodicallyDoWork(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
TestObserver<Integer> testObserver = coloObservable.test();
testObserver.awaitCount(2); // wait for a few emissions
assertThat(SomeApiClass.getSubscribedOnThread()).contains("RxNewThreadScheduler");
}
这太过分了吗?只要调用方还调用subscribeOn(),仅让interval
使用默认的计算调度程序就不会有危险吗?
答案 0 :(得分:1)
我认为,创建观察者链的API 必须提供了注入调度程序的方法。没有这种功能,单元测试将几乎无法管理。
我有很多为实时系统编写测试的经验。仅仅能够向被测单元提供TestScheduler
或两个,就可以进行合理的测试与不打扰。考虑一个debounce()
方法周期为1秒的子系统。在无法使用TestScheduler
和使用advanceTimeBy()
来控制时钟的情况下编写几十种情况的单元测试是不可行的。这意味着单元测试可以在10毫秒内完成,如果使用常规的调度程序,则需要几分钟。