简体   繁体   English

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

[英]How to mock an ObservableTransformer in unit tests using Mockito

In order to have reusable and testable rxjava code, I have separated parts of my code by using ObservableTransformers.为了获得可重用和可测试的 rxjava 代码,我使用 ObservableTransformers 分离了部分代码。 It works fine in production, however testing it is not as easy as expected since I seem to be unable to mock those ObservableTransformers.它在生产中运行良好,但是测试它并不像预期的那么容易,因为我似乎无法模拟那些 ObservableTransformers。

This is the class to test:这是要测试的类:

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();
}

Lets assume the productive MyActualObservableTransformer needs mocking in order to have a plain unit test.让我们假设富有成效的 MyActualObservableTransformer 需要模拟以进行简单的单元测试。

And this is a simple test attempting to test what happens when I hand over a new text.这是一个简单的测试,试图测试当我交出新文本时会发生什么。 I do realize the test makes no sense, it is just condensed to show my problem:我确实意识到测试没有意义,它只是浓缩以显示我的问题:

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"));
    }
}

The problem is that when running the test, the emitter has no subscribers.问题是在运行测试时,发射器没有订阅者。

Having analyzed the call stack I think I know what the problem is.分析了调用堆栈后,我想我知道问题出在哪里了。 So as far as I understand it, when subscribe is called, all subscribes of the observables within the chain are called.所以据我了解,当 subscribe 被调用时,链中所有 observable 的订阅都会被调用。 Without my ObservableTransformer mock, this is what happens and this is why it works fine in production.如果没有我的 ObservableTransformer 模拟,就会发生这种情况,这就是它在生产中运行良好的原因。

However, when I add my mock:但是,当我添加我的模拟时:

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

as soon as subscribe for it is called, it emits a new value and the subscription chain is never completed.一旦 subscribe 被调用,它就会发出一个新值并且订阅链永远不会完成。 So my question is, how can I mock an observable without it emitting a value during subscription?所以我的问题是,如何在订阅期间模拟 observable 而不发出值? I guess Observable.just or a BehaviorSubject is not the way to go since it behaves as expected in this case, as far as my still limited RxJava knowledge goes.我猜 Observable.just 或 BehaviorSubject 不是要走的路,因为它在这种情况下按预期运行,就我仍然有限的 RxJava 知识而言。 Any ideas?有任何想法吗?

The simplest solution I came up with is to just not use Mockito for mocking and mock it myself:我想出的最简单的解决方案是不使用 Mockito 进行模拟并自己模拟它:

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

I know this post is a little old, but it's the highest result for a search of "mockito ObservableTransformer", so I thought it might be a good idea to add a solution that lets you keep Mockito-ing.我知道这篇文章有点旧,但它是搜索“mockito ObservableTransformer”的最高结果,所以我认为添加一个让你保持 Mockito-ing 的解决方案可能是个好主意。

Just like when you call the transformer function in real code, you need to compose it from an Action .就像在实际代码中调用转换器函数一样,您需要从Action组合它。 The method itself doesn't return an Observable, so you have to tell it how to get there.该方法本身不返回 Observable,因此您必须告诉它如何到达那里。 You can do this by mocking the method with an ObservableTransformer lambda:您可以通过使用ObservableTransformer lambda 模拟该方法来做到这一点:

when(observableTransformer.apply())
  .thenReturn(upstream -> upstream.map(action -> "mockedString"));

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

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