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
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With