简体   繁体   English

如何单元测试多线程Android RxJava

[英]How unit test multithreaded Android RxJava

Suppose there is a button. 假设有一个按钮。

Clicking the button disables it ( mainThread thread), starts a background task to load data ( IO thread). 单击该按钮将禁用它( mainThread线程),启动后台任务以加载数据( IO线程)。 Once data is loaded, the button is enabled back ( mainThread thread). 加载数据后,按钮被启用( mainThread线程)。

For test, it's common to change schedulers to immediate , but this won't work in my case - button click will block until completion of background task, I'll never be able to check if the button was disabled after it started background task. 对于测试,通常将调度程序更改为立即 ,但这在我的情况下不起作用 - 按钮单击将阻止直到完成后台任务,我将永远无法检查按钮在启动后台任务后是否被禁用。

Besides unit tests, I'd also like to test this in functional Espresso tests. 除了单元测试,我还想在功能性Espresso测试中测试它。

How do I test this multithreaded RxJava case? 我如何测试这个多线程RxJava案例?

You can write your own ThreadFactory 你可以编写自己的ThreadFactory

ThreadFactory custom = new CustomThreadFactory();
ExecutorService executorService = Executors.newCachedThreadPool(custom); //or use newSingleThreadExecutor(..)
Scheduler customScheduler = Schedulers.from(executorService);

now you can use this scheduler and not block the main queue plus getting called when a new thread is needed: 现在你可以使用这个调度程序,而不是阻止主队列加上在需要新线程时调用:

class CustomThreadFactory implements ThreadFactory {
  public Thread lastT;
  public int newThreadCounter = 0;
  @Override
  public Thread newThread(Runnable r) {
    newThreadCounter++;
    System.out.println("newThread called");
    Thread lastT = new Thread(r); //or CustomThread(r)
    return lastT;
  }
}

You can even go further and instrument the new Thread - 您甚至可以进一步检测新线程 -

 class CustomThread extends Thread {
    public CustomThread(Runnable r) {
      super(r);
    }
    @Override
    public void run() {
      System.out.printf("About to run!");
      super.run();
    }
  }
}

I suggest you to use RxUtil 我建议你使用RxUtil

1). 1)。 Provide default implementation of RxUtil through the constructor or DI 通过构造函数或DI提供RxUtil 默认实现

2). 2)。 When you create your observable, instead of applying schedulers directly: 创建observable时,而不是直接应用调度程序:

    .subscribeOn(Schedulers.newThread())
    .observeOn(AndroidSchedulers.mainThread())

Use RxUtil : 使用RxUtil

.compose(rxUtil.applySchedulers())

Example: 例:

https://github.com/DAYTeam/GoEnglish/blob/master/app/src/main/java/ru/goenglish/goenglish/services/impl/ScheduleServiceImpl.java#L38-L44 https://github.com/DAYTeam/GoEnglish/blob/master/app/src/main/java/ru/goenglish/goenglish/services/impl/ScheduleServiceImpl.java#L38-L44

3). 3)。 In unit tests, instead of default implementation of RxUtil , create mocked version: 在单元测试中,创建RxUtil版本而不是RxUtil的默认实现:

public class UnitTestRxUtil implements RxUtil {
    @Override
    public <T> Observable.Transformer<T, T> applySchedulers() {
        return observable -> observable.subscribeOn(Schedulers.immediate())
                .observeOn(Schedulers.immediate());
    }
}

link: https://github.com/DAYTeam/GoEnglish/blob/master/app/src/test/java/ru/goenglish/goenglish/util/UnitTestRxUtil.java 链接: https//github.com/DAYTeam/GoEnglish/blob/master/app/src/test/java/ru/goenglish/goenglish/util/UnitTestRxUtil.java

4). 4)。 Pass this implementation through the constructor, or DI. 通过构造函数或DI传递此实现。
Example (constructor): https://github.com/DAYTeam/GoEnglish/blob/master/app/src/test/java/ru/goenglish/goenglish/ScheduleServiceImplTest.java#L45 示例(构造函数): https//github.com/DAYTeam/GoEnglish/blob/master/app/src/test/java/ru/goenglish/goenglish/ScheduleServiceImplTest.java#L45

As a result, all the tests, will be executed in one thread, and in the application, it will be executed on different executors 因此,所有测试都将在一个线程中执行,而在应用程序中,它将在不同的执行程序上执行

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

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