代码之家  ›  专栏  ›  技术社区  ›  dev.farmer

RxJava如何测试debounce?

  •  0
  • dev.farmer  · 技术社区  · 7 年前

    我正试图在Android中测试RxJava2的“debounce”操作符。

    我使用了“debounce”操作符作为搜索功能。

    etSearch.addTextChangedListener(object : TextWatcher {
    
        override fun afterTextChanged(editable: Editable) {
            mPresenter.search(editable.toString())
        }
    
        ...
    })
    

    演示者在这里:

    class MyPresenter(
        private val view: MyContract.View,
        private val apiService: ApiServie = ApiServiceImpl(),
        private val searchSubject: PublishSubject<String> = PublishSubject.create(),
        var debounceScheduler: Scheduler = Schedulers.computation()
    ) : MyContract.Presenter {
    
        init {
            setupSearch()
        }
    
        override fun search(keyword: String) {
            if (searchDisposable.size() == 0) {
                setupSearch()
            }
    
            searchSubject.onNext(keyword)
        }
    
        private fun setupSearch() {
            searchSubject.debounce(1000, TimeUnit.MILLISECONDS, debounceScheduler)
                    .distinctUntilChanged()
                    .switchMap { keyword ->
                        apiService.search(keyword)
                    }
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe { response ->
                        response.data?.let { data ->
                            view.searchResult(data)
                        }
                    }
        }
    }
    

    实际上,当我做集成测试时,它工作得很好。 但我想做的是测试MyPresenter类的“搜索”功能。

    article

    但它不起作用。。。

    我的测试代码在这里:

    class MyTest {
    
        @Mock
        private lateinit var mockView: MyContract.View
    
        @Mock
        private lateinit var mockApiService: ApiService
    
        private lateinit var mMyPresenter: MyPresenter
    
        private lateinit var inOrder: InOrder
    
        private val mTestScheduler: TestScheduler = TestScheduler()
    
        private val ZERO = 0
    
        private fun setUpScheduler() {
            val immediate = object : Scheduler() {
                override fun createWorker() = ExecutorScheduler.ExecutorWorker(Runnable::run)
            }
    
            RxJavaPlugins.setInitIoSchedulerHandler { immediate }
            RxAndroidPlugins.setInitMainThreadSchedulerHandler { immediate }
        }
    
        @Before
        fun setup() {
            MockitoAnnotations.initMocks(this)
            setUpScheduler()
    
            mMyPresenter = MyPresenter(mockView, mockApiService)
    
            inOrder = inOrder(mockView)
        }
    
        @Test
        fun searchBillsTest() {
            `when`(mockApiService.search("America"))
                    .thenReturn(Observable.just(mockResult))
    
            mMyPresenter.debounceScheduler = mTestScheduler
    
            mMyPresenter.search("America")
            mTestScheduler.advanceTimeBy(1000, TimeUnit.MILLISECONDS)
    
            verify(mockView).searchResult(mockResult)
        }
    }
    

    最后一次验证没有调用。。。 我在打印日志时添加了“doOnNext”以在“searchSubject”中查找原因,但“doOnNext”不被称为。。。 “doOnSubscribe”被称为。。。

    你知道为什么吗?

    1 回复  |  直到 7 年前
        1
  •  2
  •   Kh Jung    7 年前

    为了尽快通过测试代码,只需更改一些内容。

    1. 使用TestScheduler
    2. 划分 ui 和 io 调度程序。
    3. Scheduler.triggerActions 在触发主题事件之前。
    @RunWith(MockitoJUnitRunner::class)
    class DebounceTest {
        private lateinit var searchSubject: PublishSubject<String>
    
        @Mock lateinit var apiService: ApiService
    
        @Mock lateinit var presenter: Presenter
    
        private lateinit var disposable: Disposable
    
        private lateinit var ioTestScheduler: TestScheduler
        private lateinit var uiTestScheduler: TestScheduler
    
        @Before fun setUp() {
            searchSubject = PublishSubject.create<String>()
    
            ioTestScheduler = TestScheduler()
            uiTestScheduler = TestScheduler()
    
            setupSearch(uiTestScheduler, ioTestScheduler)
    
            // important https://stackoverflow.com/a/53543257/1355048
            ioTestScheduler.triggerActions()
        }
    
        @After fun tearDown() {
            disposable.dispose()
        }
    
        @Test fun searchBillsTest() {
            `when`(apiService.search("America")).thenReturn(Observable.just("MOCK RESULT"))
    
            searchSubject.onNext("America")
    
            ioTestScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
            uiTestScheduler.triggerActions()
    
            verify(presenter).doSomething("MOCK RESULT")
        }
    
        private fun setupSearch(uiScheduler: Scheduler, ioScheduler: Scheduler) {
            disposable = searchSubject.debounce(1, TimeUnit.SECONDS, ioScheduler)
                    .distinctUntilChanged()
                    .switchMap { apiService.search(it) }
                    .subscribeOn(ioScheduler)
                    .observeOn(uiScheduler)
                    .subscribe { response ->
                        presenter.doSomething(response)
                    }
        }
    
        interface ApiService {
            fun search(query: String): Observable<String>
        }
    
        interface Presenter {
            fun doSomething(result: String)
        }
    }