不依赖于Android的JUnit Scheduler

时间:2015-10-10 20:45:30

标签: unit-testing rx-java android-testing rx-android

我正在尝试使用MVP来增强单元测试并更快地运行测试(因为我测试逻辑而不是android代码,因此我避免使用像RobotElectric这样的东西)。

但我使用的是RXAndroid,它需要Looper来获取Schedulers.io()AndroidSchedulers.mainThread(),当我尝试运行某些时候

class Phone {
    public Observable<> sendSms(String number){
        //...
    }
}

Phone.getInstance().sendSms(phoneNumber)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(phone -> {
                    mView.dismissProgress();
                    mView.startCodeView(phone);
                }, error -> {
                    mView.dismissProgress();
                    mView.showError(error);
                });

我明白了:

Caused by: java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked. See http://g.co/androidstudio/not-mocked for details.
at android.os.Looper.getMainLooper(Looper.java)
at rx.android.schedulers.AndroidSchedulers.<clinit>(AndroidSchedulers.java:27)
... 28 more

我试过了:

android {
  // ...
  testOptions { 
    unitTests.returnDefaultValues = true
  }
}

但是它不起作用,因为我想运行完整的JUnit测试,而不是Roboelectric或Espresso的东西。

我怎样才能完成它?是否有任何调度程序不会因此而崩溃?

4 个答案:

答案 0 :(得分:21)

我也在使用调度程序线程,但是在我的测试SetUp和TearDown中。

@Before
public void setUp() throws Exception {
    RxAndroidPlugins.getInstance().registerSchedulersHook(new RxAndroidSchedulersHook() {
        @Override
        public Scheduler getMainThreadScheduler() {
            return Schedulers.immediate();
        }
    });
}

@After
public void tearDown() {
    RxAndroidPlugins.getInstance().reset();
}

这会有帮助吗?

答案 1 :(得分:5)

我最后为此添加了转换和“味道注入类”,有一个类使用prod / debug版本的main,并使用test flavor文件夹中的另一个类来测试Schedulers.immediate()

正常风味的类:

public class Transformer {

  public static <T> Observable.Transformer<T, T> applyIoSchedulers() {
    return observable -> observable.subscribeOn(getIoScheduler())
        .observeOn(getMainScheduler());
  }

  private static Scheduler getIoScheduler() {
    return Schedulers.io();
  }

  private static Scheduler getMainScheduler() {
    return AndroidSchedulers.mainThread();
  }
}

测试风味类:

public class Transformer {

  public static <T> Observable.Transformer<T, T> applyIoSchedulers() {
    return observable -> observable.subscribeOn(getIoScheduler())
        .observeOn(getMainScheduler());
  }

  private static Scheduler getIoScheduler() {
    return Schedulers.immediate() ;
  }

  private static Scheduler getMainScheduler() {
    return Schedulers.immediate() ;
  }
}

然后将其用于转换:

mSessionRepository.login(...)
        .compose(Transformer.applyIoSchedulers())
        .subscribe(session -> { })

答案 2 :(得分:3)

在我们的实践中,我们尽量避免在Presenter中使用#include <codecvt> #include <iostream> #include <locale> std::locale const utf8("en_US.UTF-8"); // Convert UTF-8 byte string to wstring std::wstring to_wstring(std::string const& s) { std::wstring_convert<std::codecvt_utf8<wchar_t> > conv; return conv.from_bytes(s); } // Convert wstring to UTF-8 byte string std::string to_string(std::wstring const& s) { std::wstring_convert<std::codecvt_utf8<wchar_t> > conv; return conv.to_bytes(s); } // Converts a UTF-8 encoded string to upper case std::string tou(std::string const& s) { auto ss = to_wstring(s); for (auto& c : ss) { c = std::toupper(c, utf8); } return to_string(ss); } void test_utf8(std::ostream& os) { os << tou("foo" ) << std::endl; os << tou("#foo") << std::endl; os << tou("ßfoo") << std::endl; os << tou("Éfoo") << std::endl; } int main() { test_utf8(std::cout); } ,因为它是AndroidSchedulers.mainThread()实现的详细信息。你也可以这样做。

虽然我们使用的是Robolectric,但无论如何它都可以在我们的测试中使用。

答案 3 :(得分:2)

是的,在junit测试中没有android.jar意味着没有Loopers。如果您使用Dagger,您可以将模拟调度程序注入测试,并将真正的调度程序注入源代码。您也可以使用Mockito之类的东西来模拟调度程序。另外像@Artem Zinnatullin建议的那样,Robolectric解决了这个问题。使用Android Studio很容易设置Robolectric 3。