簡體   English   中英

如何測試此rxJava代碼?

[英]How can I test this rxJava code?

void start() {
    bar.foo()
        .filter(i -> i % 2 == 0)
        .subscribeOn(computation())
        .observeOn(io())
        .subscribe(new FooSubscriber());
}

在此功能中,我看到3點要測試:

  1. 驗證我是否調用bar.foo()
  2. 驗證是否正確實施了filter
  3. 確認我已訂閱bar.foo()

第一點很容易使用Mockito.verify()進行測試。 第三點,我可以注入Schedulers並使用Schedulers.immediate() ,然后使用Subject模擬觀察者並檢查Subject.hasObservers() 但是我不知道如何測試第二點。

如何測試此代碼? 我必須重構嗎? 怎么樣?

請以為filter只是一個例子,我與不同的運營商有一個很大的聯系。

難以測試此方法,因為沒有要聲明的“可觀察”行為,並且您需要“以邏輯方式”獲取測試代碼。

您可以按照以下簡單方法進行操作:(盡管您可能需要考慮分拆事情以簡化測試)

  1. 模擬foo,驗證是否需要調用bar(),並從bar()返回一個真實的Observable,該實值將在調用subscribe時展開回調鏈。 -這將測試您的鏈條是否按預期連接。

  2. 注入調度程序以阻塞方式在主線程上執行邏輯,從而使測試保持同步並易於理解

  3. new FooSubscriber()提取到包私有方法中,並使用Mockito監視新方法,返回一個測試訂閱者,該訂閱者對可觀察到的已過濾數據進行斷言-或-注入一個工廠類來構建可模擬的FooSubscriber實例出於測試目的,返回測試訂戶。 -基本上,對new關鍵字的硬編碼使用使您無法測試行為。

如果需要,我可以提供示例,希望這可以幫助您前進。

編輯:上述兩種方法的示例:

package com.rx;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import rx.Observable;
import rx.Observer;
import rx.Scheduler;
import rx.schedulers.Schedulers;

import java.util.ArrayList;
import java.util.List;

@RunWith(MockitoJUnitRunner.class)
public class TestRxMethod {

    // prod Bar class - this class tested in isolation in different test.
    public static class Bar {

        public Observable<Integer> foo() {
            return null;
        }
    }

    // prod FooSubscriber class - this class tested in isolation in different test.
    public static class FooSubscriber implements Observer<Integer> {

        @Override
        public void onCompleted() {
        }

        @Override
        public void onError(Throwable e) {
        }

        @Override
        public void onNext(Integer t) {
        }
    }

    // prod FooSubscriberFactory class - this class tested in isolation in different test.
    public static class FooSubscriberFactory {

        public Observer<Integer> getInstance() {
            return null;
        }
    }

    // prod "class under test"
    public static class UnderTest {
        private final Bar bar;
        private final Scheduler computationScheduler;
        private final Scheduler ioScheduler;
        private final FooSubscriberFactory fooSubscriberFactory;

        public UnderTest(Bar bar, Scheduler computationScheduler, Scheduler ioScheduler,
                FooSubscriberFactory fooSubscriberFactory) {
            this.bar = bar;
            this.computationScheduler = computationScheduler;
            this.ioScheduler = ioScheduler;
            this.fooSubscriberFactory = fooSubscriberFactory;
        }

        public void start() {
            //@formatter:off
            bar.foo()
                .filter(i -> i.intValue() % 2 == 0)
                .subscribeOn(computationScheduler)
                .observeOn(ioScheduler)
                .subscribe(fooSubscriber());
            //@formatter:on
        }

        // package private so can be overridden by unit test some drawbacks
        // using this strategy like class cant be made final. - use only
        // if cant restructure code.
        Observer<Integer> fooSubscriber() {
            return fooSubscriberFactory.getInstance();
        }
    }

    // test Foo subscriber class - test will put set an instance of
    // this class as the observer on the callback chain.
    public static class TestFooSubscriber implements Observer<Integer> {

        public List<Integer> filteredIntegers = new ArrayList<>();

        @Override
        public void onCompleted() {
            // noop
        }

        @Override
        public void onError(Throwable e) {
            // noop
        }

        @Override
        public void onNext(Integer i) {
            // aggregate filtered integers for later assertions
            filteredIntegers.add(i);
        }
    }

    // mock bar for test
    private Bar bar;

    // mock foo subscriber factory for test
    private FooSubscriberFactory fooSubscriberFactory;

    // class under test - injected with test dependencies
    private UnderTest underTest;

    @Before
    public void setup() {
        bar = Mockito.mock(Bar.class);
        fooSubscriberFactory = Mockito.mock(FooSubscriberFactory.class);
        underTest = new UnderTest(bar, Schedulers.immediate(), Schedulers.immediate(), fooSubscriberFactory);
    }

    // Option #1 - injecting a factory
    @Test
    public void start_shouldWork_usingMockedFactory() {
        // setup bar mock to emit integers
        Mockito.when(bar.foo()).thenReturn(Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));

        // setup the subscriber factory to produce an instance of the test subscriber
        TestFooSubscriber testSubscriber = new TestFooSubscriber();
        Mockito.when(fooSubscriberFactory.getInstance()).thenReturn(testSubscriber);

        underTest.start();

        Assert.assertEquals(5, testSubscriber.filteredIntegers.size());
        // ... add more assertions as needed per the use cases ...
    }

    // Option #2 - spying a protected method
    @Test
    public void start_shouldWork_usingSpyMethod() {
        // setup bar mock to emit integers
        Mockito.when(bar.foo()).thenReturn(Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));

        // spy the class under test (use only as a last resort option)
        underTest = Mockito.spy(underTest);
        TestFooSubscriber testSubscriber = new TestFooSubscriber();
        Mockito.when(underTest.fooSubscriber()).thenReturn(testSubscriber);

        underTest.start();

        Assert.assertEquals(5, testSubscriber.filteredIntegers.size());
        // ... add more assertions as needed per the use cases ...
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM