I'm studying android unit testing and i'm a bit stuck of unit testing for rxjava observable.
This method i'm trying to test:
#Override
public Observable<AuthenticationUsingSqlEntity> logInUsingSql(String token, String ssid) {
SqlRepo sqlRepo = new SqlRepo();
return sqlRepo.authenticate(token, ssid);
}
I have created simple ArgumentCaptor to test that input already the same and have been passed on unit testing what i'm trying to do is to test sql retrofit response but i can't.
I wonder why you provide some method call, but not the actual SqlRepo method to test? The result is, that this only permits for a general answer. And it's unclear, which version you're talking about; I'd assume version 2. However, one can simply chain Observable with .test(), in order to test the Observable itself and there's TestObserver, but there's also TestSubscriber, which can be used to test subscribers and TestScheduler, which can be used to schedule mock RX events. It should be obvious, that the method call which've provided will not suffice to write a proper test, as the method to test is entirely unknown (to me).
In your test on that method, you shouldn't use ArgumentCaptor. Normally, it is used when you need to write a test on a method which receives or creates an object and you want to check the validity of that object as a parameter (for example, that an email address is formatted correctly).
// Create a mock of the OtherClass
OtherClass other = mock(OtherClass.class);
// Run the foo method with the mock
new A().foo(other);
// Capture the argument of the doSomething function
ArgumentCaptor<SomeData> captor = ArgumentCaptor.forClass(SomeData.class);
verify(other, times(1)).doSomething(captor.capture());
// Assert the argument
SomeData actual = captor.getValue();
assertEquals("Some inner data", actual.innerData);
The only test on that method that you can use is a test that checks if the method calls sqlRepo.authenticate. If you want to check the response from Retrofit, then you need to write a test specifically on the method 'authenticate' in the class SqlRepo. Also, if you want to write a test specifically on Observable, then you can use TestObserver. You can then use various methods to check the result you'll receive.
Here's an example:
someService.authenticate()
.test()
.assertNoErrors()
.assertValue( someValue ->
//check if the value is correct
);
Assuming your class is similar to following:
class LoginManager {
#Override
public Observable<AuthenticationUsingSqlEntity> logInUsingSql(String token, String ssid) {
SqlRepo sqlRepo = new SqlRepo();
return sqlRepo.authenticate(token, ssid);
}
}
then with unit testing we are aiming to verify the logic of a unit or function.
in that perspective the logic of your method is that you we offload the login operation to SqlRepo class. And we have to verify this logic.
So as to do that we check whether authenticate function of Sqlrepo is being invoked in response to the invokation of logInUsingSql() method.
we do that by :
loginManagerClassUnderTest = Mockito.spy(loginManagerClassUnderTest);
mockSqlRepo mockSqlRepo = Mockito.mock(mockSqlRepo.class);
doReturn(mockSqlRepo).when(loginManagerClassUnderTest).getSqlRepo();
loginManagerClassUnderTest.logInUsingsql("mockToken", "mockSsid");
ArgumentCaptor<String> tokenCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> ssidCaptor = ArgumentCaptor.forClass(String.class);
verify(mockSqlRepo).authenticate(tokenCaptor.capture(), ssidCaptor.capture());
assertEquals("mockToken", tokenCaptor.getValue());
assertEquals("mockSsid", ssidCaptor.getValue());
As you can see that LoginManager is dependent on SqlRepo class and we have to provide a mock for this dependency so that we ensure that test is carried out in isolation.
so change Loginmanager Function to :
class LoginManager {
#Override
public Observable<AuthenticationUsingSqlEntity> logInUsingSql(String token, String ssid) {
SqlRepo sqlRepo = getSqlRepo();
return sqlRepo.authenticate(token, ssid);
}
public SqlRepo getSqlRepo() {
return new SqlRepo();
}
}
Related
I have a MVVM architecture in my Android app. In an activity, I invoke a method to try to create something from service/repository and return it. I am using RxJava.
Here is the flow:
I click something in view, it invokes method in the Activity.
Method in Activity invokes method in ViewModel.
Method in ViewModel invokes method in Interactor(/use-case).
Interactor has access to service and tries to create something from that service.
Here is the code for this:
Activity:
#Override
public void onCreateWalletClick(String password) {
addWalletViewModel.createWallet(password);
}
ViewModel:
public class AddWalletViewModel extends BaseViewModel {
private AddWalletInteractor addWalletInteractor;
private final MutableLiveData<Wallet> newWallet = new MutableLiveData<Wallet>();
private final MutableLiveData<ErrorCarrier> newWalletError = new MutableLiveData<ErrorCarrier>();
public LiveData<Wallet> newWallet() {
return newWallet;
}
public AddWalletViewModel(AddWalletInteractor addWalletInteractor) {
this.addWalletInteractor = addWalletInteractor;
}
public Single<Wallet> createWallet(String password){
return addWalletInteractor.addWallet(password)
.subscribe(wallet -> newWallet.postValue(wallet), this::addErrorToLiveData);
}
private void addErrorToLiveData(Throwable throwable){
newWalletError.postValue(new ErrorCarrier());
}
}
Interactor:
public class AddWalletInteractor {
private final KeyStoreServiceInterface keyStoreServiceInterface;
public AddWalletInteractor(KeyStoreServiceInterface keyStoreServiceInterface) {
this.keyStoreServiceInterface = keyStoreServiceInterface;
}
public Single<Wallet> addWallet(String password){
return keyStoreServiceInterface.
createWalletAndReturnWallet(password);
}
}
Service:
#Override
public Single<Wallet[]> getAllWallets() {
return Single.fromCallable(()-> {
Accounts accounts = keyStore.getAccounts();
int amount = (int) accounts.size();
Wallet[] wallets = new Wallet[amount];
for (int i = 0; i<amount; i++){
org.ethereum.geth.Account gethAccount = accounts.get(i);
wallets[i] = new Wallet(gethAccount.getAddress().getHex().toLowerCase());
}
return wallets;
}).subscribeOn(Schedulers.io());
}
Problem is I can not manage to get this to work by tweaking the code. Right now it forces me to cast to (Single) in the return of the createWallet() method in the viewmodel. When running the app, it crashes in that method with:
java.lang.ClassCastException:
io.reactivex.internal.observers.ConsumerSingleObserver cannot be cast
to io.reactivex.Single
at addwallet.AddWalletViewModel.createWallet(AddWalletViewModel.java:31)
Please keep in mind I am new to RxJava, I am still trying to figure it out. Any suggestions here?
The cast performed in the createWallet method will always fail.
Solution 1
The simplest way to fix the crash is to change the return type of that method to io.reactivex.disposables.Disposable, assuming you're using RxJava 2. If you're using RxJava 1, then have it return rx.Subscription. The code you presented that calls the createWallet method doesn't seem to use the returned value so it shouldn't make a difference.
Solution 2
If you really do need the return type to be Single and you want to keep the same behavior, then an alternate solution would be to change the createWallet method as follows:
public Single<Wallet> createWallet(String password) {
return addWalletInteractor.addWallet(password)
.doOnSuccess(wallet -> newWallet.postValue(wallet))
.doOnError(this::addErrorToLiveData);
}
The method now returns a new Single that does whatever the Single returned from addWallet does and additionally invokes the appropriate lambda function when a value is successfully emitted or an error occurs. You would also need to modify the call site for the method as follows:
#Override
public void onCreateWalletClick(String password) {
addWalletViewModel.createWallet(password).subscribe();
}
That subscribe call is needed to have the Single start emitting values. It takes no parameters because you already do all of the interesting work in the createWallet method itself. Both snippets were written with RxJava 2 in mind, but I believe they will also work in RxJava 1 as is.
If you haven't already done so, you should check out the official Rx website as it provides a ton of information on how reactive streams work and how to use them.
Since you're new to RxJava and the documentation is so vast, here's a brief overview of the subscription concept and how it applies to your situation.
RxJava and other stream-based libraries like it have two main components: producers and consumers. Producers supply values and consumers do something with those supplied values.
Single is a kind of producer that only produces one value before terminating. In your case, it produces a reference to the newly created wallet. In order to do something with that reference, it needs to be consumed. That's what the subscribe method on the Single class does. When the Single returned by the addWallet method produces a value, the lambda passed to the subscribe method is invoked and the wallet parameter in that lambda is set to the produced value.
The return type of the subscribe method is NOT itself a Single. When a consumer and a producer are coupled together by the subscribe method, it forms a connection which is represented by the Disposable class. An instance of that class has methods to cancel the connection before the producer is done producing values or to check if the connection has been cancelled. It is this connection object that is returned by the subscribe method.
Note that until this connection is made via one of the subscribe overloads, the producer will not start producing items. I.e., a Single that is never subscribed to will never do anything. It's analogous to a Runnable whose run method is never called.
I'm unit testing a class - AuthenticationService - responsible for authenticating the user with Firebase Auth. I'm using JUnit, Mockito and PowerMock for this purpose.
I'm mocking completely Firebase Auth, as my main target is the logic contained within the class. My problem resides in this method:
public void loginWithEmailAndPassword(String email, String password, OnCompletedListener listener) {
if (Strings.isNullOrEmpty(email) || !Pattern.compile(EMAIL_PATTERN).matcher(email).matches()) {
throw new IllegalArgumentException("email field is empty or bad formatted");
}
if (Strings.isNullOrEmpty(password)) {
throw new IllegalArgumentException("password field must be not empty");
}
mFirebaseAuth.signInWithEmailAndPassword(email, password)
.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
if (listener != null)
listener.onComplete(new AuthResult(true, null));
} else {
Exception exception = (FirebaseAuthException)task.getException();
Log.e(TAG, exception.getMessage());
if (listener != null) {
AuthResult result = new AuthResult(false, createStatusFromFirebaseException(exception));
listener.onComplete(result);
}
}
});
}
I want to test the lambda method passed in addOnCompleteListener.
I know i need to somehow call this lambda method, because Firebase itself will never call it, i'm mocking Firebase after all.
The problem is: i don't have any idea of how to call this lambda method in my unit test code.
I need to test if the onComplete method is being called in the listener, and it's arguments.
Thanks in advance.
You've having difficulty doing true unit testing here because you're actually trying to test two classes, not just one. Pure unit tests only work with a single class under test - everything else is mocked or stubbed.
The second class here that's not immediately obvious is the OnCompleteListener that you're passing to mFirebaseAuth.signInWithEmailAndPassword().addOnCompleteListener(). If you factor that out and test it separately, your outer class will become possible to properly unit test.
If you do this, now you have an opportunity to also unit test the OnCompleteListener on its own by passing it a mocked Task and checking if it does the right thing with it and the mock OnCompletedListener that you're passing to loginWithEmailAndPassword.
To summarize: you have a fair amount of refactoring to do here to make this class able to be properly unit tested.
in my view I have a function which called from presenter with my object, I write a unit test which checks the type of arrived object in that function but how can I check the items in the arrived object(MyModel).
for example, I want to check that the list in my object has 2 items?
in mock View class
final Action1<MyModel> myAction = mock(Action1.class);
#Override
public Func1<Observable<MyModel>, Subscription> invalidAppWidgetIdWidgetModal() {
return RxUi.ui(myAction, Schedulers.immediate());
}
in test method
verify(mViewMock.myAction()).call(any(MyModel.class));
I found the solution, need to use ArgumentCaptor
ArgumentCaptor<MyModel> viewModelCapture = ArgumentCaptor.forClass(MyModel.class);
verify(mViewMock. myAction()).call(viewModelCapture.capture());
assertThat(viewModelCapture.getValue().getListData().size(), is(2));
I am trying to write some unit tests for an Android application that is using Retrofit 2, Mockito 1.10 and RXJava 1.0. I am not using a java version that supports lambdas!
My code uses Observables and I can do the following:
when(myAPI.Complete(anyString(), any(MyContainer.class)))
.thenReturn(Observable.<GenericResponse>error(new Throwable("An error has occurred!")));
Subscriber genericResponseSubscriber = mock(Subscriber.class);
myPresenter.myUseCase(id, container, genericResponseSubscriber);
verify(genericResponseSubscriber, times(1)).onError(any(Throwable.class));
The above code works fine and allows me to throw an error and capture it within the test.
What I need to be able to do as well (of course) :) is to capture positive conditions. I feel like it's obvious but can't find the answer I need.
How can I capture onComplete and onNext cases ?
I know that the verification for onComplete would be...
verify(genericResponseSubscriber, times(1)).onCompleted();
But I can't see what my 'when' clause should be. I tried the following but that fails:
GenericResponse response = new GenericResponse();
response.setSuccess(true);
when(myAPI.orderComplete(anyString(), any(MyContainer.class)))
.thenReturn(Observable.just(response));
Subscriber genericResponseSubscriber = mock(Subscriber.class);
myPresenter.myUseCase(id, container, genericResponseSubscriber);
verify(genericResponseSubscriber, times(1)).onCompleted();
The failure here is that subscriber.onStart() was instead called.
So, what I would like to know is, how I can mock and verify the 'onComplete' and 'onNext' calls, please and more importantly what I should have looked to be able to have resolved this myself rather than having to ask! :)
As always, any help is appreciated.
Edit..
My onError working test case..
public void UseCaseOnError() throws Exception {
String id = "5";
order Order = new Order();
SomeContainer myContainer = new SomeContainer(order);
when(myRetroFitAPI.complete(anyString(), any(SomeContainer.class)))
.thenReturn(Observable.error(new Throwable(“My error!")));
Subscriber genericResponseSubscriber = mock(Subscriber.class);
orderPresenter.doUseCase(id, myContainer, genericResponseSubscriber);
verify(genericResponseSubscriber,times(1)).onError(any(Throwable.class));
}
What I should really add is that, I feel there should be an equivalent for onError in terms of a positive state, i.e. onCompleted. If I do the same but with onCompleted instead, my verification fails as it detects onStart has been called instead which I am finding rather confusing.
I have tried using the ReplaySubject as such:
public void createOrderOnCompleteError() {
orderOnCompleteSubject.onError(new Throwable("I am an error"));
}
public void createOrderOnCompleteSuccess() {
orderOnCompleteSubject.onNext(new GenericResponse().setSuccess(true));
orderOnCompleteSubject.onCompleted();
}
The error mechanism works fine.. the completed mechanism does not...
You should use the class TestObserver for testing the Observable, in this way:
public Observable<Integer> getObservable() {
return Observable.just(12, 20, 330);
}
#Test
public void testObservable() {
Observable<Integer> obs = getObservable();
TestObserver<Integer> testObserver = TestObserver.create();
obs.subscribe(testObserver);
testObserver.assertComplete();
testObserver.assertResult(12, 20, 330);
}
In this way you can verify that it completes and emits all the expected items.
If you want to create a mocked version of your observable, you can just create a new Observable that has the behaviour that you want. For example:
public Observable<Integer> mockedObservableCompleteWithResult() {
return Observable.create(e -> {
e.onNext(12);
e.onNext(20);
e.onNext(330);
e.onComplete();
});
}
that can be verified with the above-mentioned test.
Then we can create other mock for modelling other results
public Observable<Integer> mockedObservableError() {
return Observable.create(e -> {
e.onNext(12);
e.onError(new Throwable("Generic exception"));
});
}
That can be verified:
#Test
public void testObservable() throws Exception {
Observable<Integer> obs = mockedObservableError();
TestObserver<Integer> testObserver = TestObserver.create();
obs.subscribe(testObserver);
testObserver.assertError(Throwable.class);
}
Instead of mocking the Subscriber, you should create a TestSubscriber for RxJava 1:
when(myAPI.Complete(anyString(), any(MyContainer.class)))
.thenReturn(Observable.<GenericResponse>error(new Throwable("An error has occurred!")));
TestSubscriber genericResponseSubscriber = TestSubscriber.create();
myPresenter.myUseCase(id, container, genericResponseSubscriber);
// To check for an error
genericResponseSubscriber.assertError(Throwable.class)
// To check for completion
genericResponseSubscriber.assertCompleted()
You might need to be a bit more specific about which error class you expect. Check out the TestSubscriber documention. There is tons of more stuff you can verify with this class.
Happy testing!
The easy way is to try throwing a mock exception than the real one.
#Mock
Exception mockException;
observer.onError(mockException);
Firstly, my setup is RXJava 1, Retrofit 2 and I'm using Java 7.
I have a method that, when it is called, will set an atomic boolean to true.
That method then calls a retrofit API.
Upon completion, timeout etc... the atomic boolean is reset to false.
So, I would therefore like to uni test that when I call my method, the Atomic Boolean is set to true.
So, I do the following:
assertFalse(orderUseCase.isOrderInProcess());
orderUseCase.execute(id, orderWrapper, ts);
assertTrue(orderUseCase.isOrderInProcess());
Test that the boolean is false.
Execute my use case
Test that the boolean is true.
Now, in order to perform the last test I need to ensure the API does nothing when it is called (the execute method will call the retrofit API.
To try and doNothing I am using the following line at the start of my test case.
doNothing().when(orderAPI.orderComplete(anyString(), any(OrderWrapper.class)));
I am however receiving the following error:
org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unfinished stubbing detected here:
-> at com.tfds.xms.unit_test.SingleTest.TestAtomicBooleanLocked(SingleTest.java:90)
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, you naughty developer!
3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed
From reading where other people had a similar issue, the answers were suggesting that their 'when' line was calling another Mock (which I don't believe I am doing) or that they hadn't done the 'PrepareForTest' step which I have done. I am however wondering if what is in my prepareForTest section is correct or not...
Incidentally I understand that the error is pointing me to add a 'thenReturn' but that would mean that the API 'does' something and would consequently cause the Atomic Boolean to unlock again, which is not what I want..
Any help is very much appreciated.
My full test case is:
#RunWith(TestRunner.class)
#PrepareForTest({OrderApiService.class, OrderUseCase.class})
public class SingleTest {
#Rule
public RxJavaResetRule pluginsReset = new RxJavaResetRule();
private OrderApiService orderAPI;
private OrderUseCase orderUseCase;
private OrderRepository orderRepository;
#Mock App app;
#Before
public void setUp() {
orderAPI = mock(OrderApiService.class);
orderRepository = new OrderRepository(app, orderAPI);
orderUseCase = new OrderUseCase(orderRepository);
}
#Test
public void TestAtomicBooleanLocked() throws Exception {
doNothing().when(orderAPI.orderComplete(anyString(), any(OrderWrapper.class)));
String id = "5";
Order order = new Order();
OrderWrapper orderWrapper = new orderWrapper(order);
TestSubscriber<GenericResponse> ts = new TestSubscriber<GenericResponse>();
assertFalse(orderUseCase.isOrderInProcess());
orderUseCase.execute(id, orderWrapper, ts);
assertTrue(orderUseCase.isOrderInProcess());
}
}
The doNothing() method from Mockito works a bit different then the standalone when() method. You should be fine by just pushing the method call out of the .doNothing() like this:
doNothing().when(orderAPI)
.orderComplete(anyString(), any(OrderWrapper.class));
Happy testing!