繁体   English   中英

单元测试Android应用程序依赖于Robolectric / RxJava / RxAndroid

[英]Unit Testing Android app that relies on Robolectric / RxJava / RxAndroid

我正在使用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 ),则登录按钮文本应该与名为R.string.button_login的字符串资源的值相同。

更改按钮状态的设置在我的Activity中完成,如下所示:

        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中的更改之前触发。 我确实在调试器中看到Observer最终会触发,并且在signInButton上正确设置了文本。 我认为使用立即调度程序会使所有事情发生,就像我在调用setText后直接发生的那样。

在声明之前添加以下内容。 这个对我有用。

    // 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);

我在Observables中遇到了同样的问题。 我的方式是使用

usernameAvailable("username").toBlocking().first();

这将在测试中同步给出可观察的第一个结果。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM