samedi 14 février 2015

Unit Testing Android app that relies on RxJava / RxAndroid

I have a test:



@Test
public void valid_login_transitions_to_main_app() throws Exception {
when(testReferenceManager.getUserServiceMock().checkUsernameAvailability(anyString())).thenReturn(Observable.just(Arrays.asList(new User("test@email.com", "password"))));
when(testReferenceManager.getUserServiceMock().checkAuthStatus(anyString())).thenReturn(Observable.just(Arrays.asList(new User("test@email.com", "password"))));

EditText emailText = (EditText)activity.findViewById(R.id.text_email);
EditText passwordText = (EditText)activity.findViewById(R.id.text_password);
Button signInButton = (Button)activity.findViewById(R.id.sign_in_button);

emailText.setText("test@email.com");
passwordText.setText("password");


assertThat(signInButton.getText()).isEqualTo(App.R.getString(R.string.button_login));
}


The key thing here is that if the API reports a user exists (which it does from the mocks above) the sign in button text should be the same as the value of the string resource named R.string.button_login.


The setup for changing the button state is done in my Activity like this:



ReactiveEditText.textObservableForTextView(emailText)
.startWith(emailText.getText().toString())
.subscribe(new EndlessObserver<String>() {
@Override
public void onNext(String username) {
setSignInEnabledState();
webServices.usernameAvailable(username)
.observeOn(Schedulers.immediate())
.subscribe(new EndlessObserver<Boolean>() {
@Override
public void onNext(Boolean available) {
usernameIsAvailable = available;
if (available) {
signInButton.setText(getString(R.string.button_signup));
} else {
signInButton.setText(getString(R.string.button_login));
}
}
});
}
});


ReactiveEditText.textObservableForTextView simply wraps the textChangedListener interface in a reactive fashion:



public static Observable<String> textObservableForTextView(final TextView tv) {
return Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(final Subscriber<? super String> subscriber) {
tv.addTextChangedListener(new TextWatcher() {
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override public void afterTextChanged(Editable s) { }

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
subscriber.onNext(s.toString());
}
});
}
});
}


Notice that I am using the 'immediate' scheduler to listen for changes to the EditText. My confusion is this: the unit test always fails, and it seems to me (when I jump into the debugger) that the assertThat statement in the test is firing before the observer ever sees a change in the EditText. I do see in the debugger that the Observer does eventually fire and the text is properly set on the signInButton. I thought using the immediate scheduler would make everything happen as I expect directly after setText is called.


Aucun commentaire:

Enregistrer un commentaire