RxJava Unittests

时间:2017-08-04 09:14:02

标签: unit-testing junit rx-java

我无法测试我的RxJava代码,因为它似乎总是在测试中运行而不等待调用subscribe()时应该执行的执行。

这是要测试的存储库:

public class Repository<T extends Entity> {

    private final DataSource<T> remoteDataSource;
    private final DataSource<T> localDataSource;

    public Repository(DataSource<T> remoteDataSource, DataSource<T> localDataSource) {
        this.remoteDataSource = remoteDataSource;
        this.localDataSource = localDataSource;
    }

    public Observable<Boolean> save(T entity) {

        return localDataSource
                .save(entity)
                .flatMap(success -> {
                    if (success) {
                        return remoteDataSource.save(entity);
                    }
                    return Observable.just(Boolean.FALSE);
                })
                .doOnNext(success -> {
                    if (success) {
                        if (cache == null) {
                            cache = new LinkedHashMap<>();
                        }
                        cache.put(entity.getId(), entity);
                    }
                });
    }
}

这是DataSource接口:

public interface DataSource<T extends Entity> {
    <T> Observable<T> get();

    Observable<Boolean> save(T entity);

    Observable<Boolean> clear();

    Observable<Boolean> remove(T entity);
}

这是单元测试,包括一个MockDataSource,它应该用不同的执行时序模拟数据访问:

public class RepositoryTest {

    @Test
    public void testRepository() {

        MockSource remoteSource = new MockSource("RemoteSource", 1000L);
        MockSource localSource = new MockSource("LocalSource", 200L);
        Repository<Poi> poiRepository = new Repository<>(remoteSource, localSource);

        Poi poi1 = newMockPoi();

        Observable<Boolean> obs = poiRepository.save(poi1);
        TestSubscriber<Boolean> testSubscriber = new TestSubscriber<>();
        obs.subscribe(testSubscriber);

        testSubscriber.assertNoErrors();
        testSubscriber.assertReceivedOnNext(Arrays.asList(true));


    }

    private Poi newMockPoi() {
        Poi poi = new Poi();
        poi.name = RandomStringUtils.randomAlphabetic(12);
        poi.description = RandomStringUtils.randomAlphabetic(255);
        poi.latitude = new Random().nextDouble();
        poi.longitude = new Random().nextDouble();
        return poi;
    }


    private class Poi extends Entity {
        String name;
        String description;
        Double latitude;
        Double longitude;
    }

    private class MockSource implements DataSource<Poi> {

        private String name;
        private final long delayInMilliseconds;

        private Map<Long, Poi> pois = new LinkedHashMap<>();

        private MockSource(String name, long delayInMilliseconds) {
            this.delayInMilliseconds = delayInMilliseconds;
            this.name = name;
        }

        @Override
        public Observable<List<Poi>> get() {
            return Observable
                    .zip(
                            Observable
                                    .just(pois)
                                    .map(Map::entrySet)
                                    .flatMapIterable(entries -> entries)
                                    .map(Map.Entry::getValue)
                                    .toList(),
                            Observable
                                    .interval(delayInMilliseconds, TimeUnit.MILLISECONDS), (obs, timer) -> obs)
                    .doOnNext(pois -> System.out.println("Soure " + name + " emitted entity"));
        }

        @Override
        public Observable<Boolean> save(Poi entity) {
            return Observable
                    .zip(
                            Observable.just(true).asObservable(),
                            Observable.interval(delayInMilliseconds, TimeUnit.MILLISECONDS), (obs, timer) -> obs)
                    .doOnNext(value -> pois.put(entity.getId(), entity))
                    .doOnNext(pois -> System.out.println("Soure " + name + " saved entity"));
        }

        @Override
        public Observable<Boolean> clear() {
            return Observable
                    .zip(
                            Observable.just(true).asObservable(),
                            Observable.interval(delayInMilliseconds, TimeUnit.MILLISECONDS), (obs, timer) -> obs)
                    .doOnNext(value -> pois.clear())
                    .doOnNext(pois -> System.out.println("Soure " + name + " cleared all entities"));
        }

        @Override
        public Observable<Boolean> remove(Poi entity) {
            return Observable
                    .zip(
                            Observable.just(true).asObservable(),
                            Observable.interval(delayInMilliseconds, TimeUnit.MILLISECONDS), (obs, timer) -> obs)
                    .doOnNext(value -> pois.remove(entity))
                    .doOnNext(pois -> System.out.println("Soure " + name + " removed entity"));
        }
    }
}

这是输出:

java.lang.AssertionError: Number of items does not match. Provided: 1  
Actual: 0.
Provided values: [true]
Actual values: []
 (0 completions)

at rx.observers.TestSubscriber.assertionError(TestSubscriber.java:667)
at rx.observers.TestSubscriber.assertReceivedOnNext(TestSubscriber.java:320)
at nl.itc.geofara.app.data.source.RepositoryTest.testRepository(RepositoryTest.java:37)

另外,在存储库的save方法中设置断点并在调试模式下运行会显示例如永远不会调用.flatMap(success - &gt; ...)。

1 个答案:

答案 0 :(得分:2)

您在可观察执行和JUnit线程之间遇到 竞争条件 ,因为您在模拟源中使用的Observable.interval引入了implicit subscription on computation scheduler }:

  

调度程序:   默认情况下,interval在计算Scheduler上运行。

此外,请参阅this answer,&#34;疑难解答&#34;,&#34;竞争条件&#34;中的更多解释。