如何使用 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"));
为了拥有可重用和可测试的 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"));