我使用的是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;
}
});
}
请注意,我正在使用&#39;立即&#39;调度程序监听EditText的更改。我的困惑是:单元测试总是失败,在我看来(当我跳进调试器时),测试中的assertThat
语句在观察者看到EditText发生变化之前就开始了。我确实在调试器中看到Observer最终会触发,并且在signInButton上正确设置了文本。我认为使用立即调度程序会使所有事情都发生,因为我希望在setText
被调用之后直接发生。
答案 0 :(得分:2)
在声明之前添加以下内容。它对我有用。
// Flush all worker tasks out of queue and force them to execute.
Robolectric.flushBackgroundThreadScheduler();
Robolectric.getBackgroundThreadScheduler().idleConstantly(true);
// A latch used to lock UI Thread.
final CountDownLatch lock = new CountDownLatch(1);
try
{
lock.await(millisecond, TimeUnit.MILLISECONDS);
}
catch (InterruptedException e)
{
lock.notifyAll();
}
// Flush all UI tasks out of queue and force them to execute.
Robolectric.flushForegroundThreadScheduler();
Robolectric.getForegroundThreadScheduler().idleConstantly(true);
答案 1 :(得分:1)
我遇到了与Observables相同的问题。我的方式是使用
usernameAvailable("username").toBlocking().first();
这将在测试中同步给出可观察的第一个结果。