Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to assert if a Completable has been subscribed/completed (RxJava2)

I'm having trouble identifying if a Completable has been subscribed to in my tests, for example:

interface IWebApi {
    Observable<Data> download();
}

interface IDataRepository {
    Completable insert(Data data);
}

class SyncService {

    IWebApi webApi;
    IDataRepository repository;

    public SyncService(IWebApi webApi, IDataRepository repository) {
        this.webApi = webApi;
        this.repository = repository;
    }

    public Completable sync() {
        return webApi.download()
            .flatMapCompletable((Data data) -> { repository.insert(data) })
    }
}

And then in my tests:

@Test
public void syncTest() {
    Data data = new Data();
    IDataRepository repository = mock (IDataRepository.class);
    IWebApi webApi = mock (IWebApi.class);

    given(webApi.download()).willReturn(Observable.just(data));
    given(repository.insert(data)).willReturn(Completable.complete());

    TestObserver<Void> observer = new TestObserver<Void>();
    SyncService service = new SyncService(webApi, repository);
    service.sync()
            .subscribe(observer);

    observer.assertComplete();
    verify(repository).insert(data);
}

This test will pass. But I Could rewrite the sync method without using flatMapCompletable like this:

    public Completable sync() {
        return webApi.download()
            .doOnNext((Data data) -> { repository.insert(data) })
            .ignoreElements();
    }

Then my tests would pass but code would not work because even thought I've called the insert method, I've not called subscribe() in it.

How should I proceed about this?

P.S I am new to RxJava so if I am not using the best practices I would love to know :)

Update

Fixed the mistake of not calling .ingnoreElements() pointed by Maxim Ostrovidov

like image 256
Patrick dos Santos Avatar asked Jan 17 '17 13:01

Patrick dos Santos


1 Answers

You can use test() operator for convinience:

SyncService service = new SyncService(webApi, repository);
TestObserver observer = service.sync().test();

But I Could rewrite the sync method without using flatMapCompletable like this:

public Completable sync() {
    return webApi.download()
        .doOnNext((Data data) -> { repository.insert(data) })
}

This won't compile because doOnNext is used only to invoke actions on items, not changing the stream return type. In your case method is expecting Completable but actually it will be Observable<Data>.

Even if you force change the eventual stream type:

public Completable sync() {
    return webApi.download()
        .doOnNext((Data data) -> { repository.insert(data) })
        .ignoreElements(); //converts to Completable
}

repository.insert(data) will not be invoked because doOnNext is not subscribing to anything you passed and not returning anything:

//under the hood of lambdas
.doOnNext(new Consumer<Data>() {
    @Override
    public void accept(Data data) throws Exception {

    }
})

Your initial code is the best for what you want to achieve:

public Completable sync() {
    return webApi.download()
        .flatMapCompletable((Data data) -> { repository.insert(data) })
}

flatMapCompletable subscribes to passed Completable using items emitted by Observable:

.flatMapCompletable(new Function<Data, CompletableSource>() {
    @Override
    public CompletableSource apply(Data data) throws Exception {
        return repository.insert(data);
    }
})

Edit

To test the fact of repository.insert subscription you can use another TestObserver and Completable with applied doOnSubscribe:

TestObserver repositoryInsertObserver = TestObserver.create();
Completable insertCompletable = Completable.complete()
    .doOnSubscribe(d -> repositoryInsertObserver.onSubscribe(d));

//pass completable to your mock
given(repository.insert(data)).willReturn(insertCompletable);

//and verify that subscription took place
repositoryInsertObserver.assertSubscribed();
like image 180
Maksim Ostrovidov Avatar answered Oct 12 '22 06:10

Maksim Ostrovidov