如何使用 Mockito 在单元测试中模拟 ObservableTransformer

How to mock an ObservableTransformer in unit tests using Mockito

为了拥有可重用和可测试的 rxjava 代码,我使用 ObservableTransformers 分离了部分代码。它在生产中运行良好,但是测试它并不像预期的那么容易,因为我似乎无法模拟那些 ObservableTransformer。

这是要测试的class:

import io.reactivex.Observable;
import io.reactivex.ObservableTransformer;
import io.reactivex.subjects.PublishSubject;

public class Cut {

    private PublishSubject<String> emitter = PublishSubject.create();
    private ITransformerProvider transformerProvider;

    public Cut(ITransformerProvider transformerProvider) {
        this.transformerProvider = transformerProvider;
    }

    public void writeNewText(String text){
        emitter.onNext(text);
    }

    public Observable<String> getActualText(){
        return emitter
                .compose(transformerProvider.getObservableTransformer());
    }

    class MyActualObservableTransformer implements ITransformerProvider {
        @Override
        public ObservableTransformer<String, String> getObservableTransformer() {
            //does something I do not want to execute in unit test, e.g. REST call
            return null;
        }
    }
}
import io.reactivex.ObservableTransformer;

public interface ITransformerProvider {
    ObservableTransformer<String, String> getObservableTransformer();
}

让我们假设高效的 MyActualObservableTransformer 需要模拟才能进行简单的单元测试。

这是一个简单的测试,试图测试当我交出新文本时会发生什么。我确实意识到测试没有意义,它只是浓缩来显示我的问题:

public class TextTest {

    @Mock
    private ITransformerProvider transformerProvider;
    @Mock
    private ObservableTransformer<String, String> observableTransformer;

    private TestObserver<String> testObserver;
    private Cut cut;


    @Before
    public void setup(){
        MockitoAnnotations.initMocks(this);

        when(transformerProvider.getObservableTransformer()).thenReturn(observableTransformer);
        when(observableTransformer.apply(any())).thenReturn(Observable.just("mockedText"));

        testObserver = new TestObserver<>();
        cut = new Cut(transformerProvider);

        cut.getActualText()
            .observeOn(Schedulers.trampoline())
            .subscribe(
                    newText -> testObserver.onNext(newText)
                    , error -> Assert.fail(error.getMessage()));
    }

    @Test
    public void testGetActualText(){
        cut.writeNewText("ignoredText");
        testObserver.assertValueAt(0, text -> text.equals("mockedText"));
    }
}

问题是运行测试时,发射器没有订阅者。

分析了调用堆栈后,我想我知道问题出在哪里了。据我所知,当调用 subscribe 时,链中所有可观察对象的订阅都会被调用。如果没有我的 ObservableTransformer 模拟,就会发生这种情况,这就是它在生产中运行良好的原因。

但是,当我添加模拟时:

when(observableTransformer.apply(any())).thenReturn(Observable.just("mockedText"));

只要调用它的订阅,它就会发出一个新值,并且订阅链永远不会完成。 所以我的问题是,如何在订阅期间不发出值的情况下模拟一个可观察对象?我想 Observable.just 或 BehaviorSubject 不是可行的方法,因为在这种情况下它的行为符合预期,就我仍然有限的 RxJava 知识而言。有什么想法吗?

我想到的最简单的解决方案就是不使用 Mockito 进行模拟并自己模拟它:

private ObservableTransformer observableTransformer = text -> text.map(ignore -> "mockedText");

我知道这个 post 有点旧,但它是搜索“mockito ObservableTransformer”的最高结果,所以我认为添加一个让您保持 Mockito-ing.

就像你在真正的代码中调用transformer函数一样,你需要从一个Action中组合它。该方法本身不是 return Observable,因此您必须告诉它如何到达那里。您可以通过使用 ObservableTransformer lambda:

模拟方法来做到这一点
when(observableTransformer.apply())
  .thenReturn(upstream -> upstream.map(action -> "mockedString"));