Robolectric 3.0 スナップショットを使用しています。
私はテストを持っています:
@Test
public void my_test() 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");
Robolectric.flushBackgroundScheduler();
Robolectric.flushForegroundScheduler();
assertThat(signInButton.getText()).isEqualTo(App.R.getString(R.string.button_login));
}
ここで重要なことは、API がユーザーが存在することを報告する場合 (上記のモックから行う)、サインイン ボタンのテキストは、という名前の文字列リソースの値と同じにする必要があるということR.string.button_login
です。
ボタンの状態を変更するためのセットアップは、次のようにアクティビティで行われます。
ReactiveEditText.textObservableForTextView(emailText)
.startWith(emailText.getText().toString())
.switchMap(new Func1<String, Observable<Boolean>>() {
@Override
public Observable<Boolean> call(String username) {
return webServices.usernameAvailable(username);
}
})
.subscribe(new EndlessObserver<Boolean>() {
@Override
public void onNext(Boolean available) {
if (available) {
signInButton.setText(getString(R.string.button_signup));
} else {
signInButton.setText(getString(R.string.button_login));
}
}
});
ReactiveEditText.textObservableForTextView は、単純に textChangedListener インターフェイスをリアクティブな方法でラップします。
public static Observable<String> textObservableForTextView(final TextView tv) {
Observable<String> textObservable = 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());
}
});
}
});
return ViewObservable.bindView(tv, textObservable);
}
usernameAvailable は上記のモック化されたデータを受け取り、次のようになります。
// If the incoming array is size 0 the username is available, otherwise it already exists.
public Observable<Boolean> usernameAvailable(final String username) {
return userService.checkUsernameAvailability(username)
.map(new Func1<List<User>, Boolean>() {
@Override
public Boolean call(List<User> usersMatchingUsername) {
return usersMatchingUsername.size() == 0;
}
});
}
EditText への変更をリッスンするために「即時」スケジューラを使用していることに注意してください。私の混乱はこれです: 単体テストは常に失敗し、(デバッガーにジャンプすると)assertThat
オブザーバーが EditText の変更を確認する前にテスト内のステートメントが起動しているように見えます。オブザーバーが最終的に起動し、テキストが signInButton に適切に設定されていることをデバッガーで確認できます。setText
即時スケジューラを使用すると、呼び出された直後に期待どおりにすべてが起こると思いました。