我正在努力研究如何在具有许多运算符的RxJava流中测试复杂逻辑。我发现如果我有复杂的逻辑和许多具有复杂逻辑的操作符串联在一起测试流的单元成为一场噩梦,因为有很多案例组合需要覆盖。
以下是我所描述的一个例子:
Observable<ObjectA> observable = myRepository
.fetchA()
.flatMap(new Func1<ObjectA, Observable<ObjectA>>() {
@Override
public Observable<ObjectA> call(final ObjectA object) {
if (object.isAvailable()) {
//do something
return myRepository
.fetchAnotherThing();
} else {
//do something else
return myRepositor
.fetchADifferentThing();
}
}
})
.flatMap(new Func1<ObjectA, Observable<ObjectA>>() {
@Override
public Observable<ObjectA> call(final ObjectA object) {
if (object.isFull()) {
//do something
return myRepository
.doThingA();
} else {
//do something else
return myRepository
.doThingB();
}
}
});
如果我想为此编写单元测试,我需要编写至少4种测试方法,以便我可以覆盖条件逻辑的所有组合。
通常在发生类似这样的事情时,我会编写一个自定义类来处理逻辑,然后对该类进行单元测试。所以上面的代码可能变成这样的东西:
public class MyFunc implements Func1<ObjectA, Observable<ObjectA>> {
@Override
public Observable<ObjectA> call(final ObjectA object) {
//The logic that was formerly in an anonymous Func1 is now here
if (object.isAvailable()) {
//do something
return myRepository.fetchAnotherThing();
} else {
//do something else
return myRepository.fetchADifferentThing();
}
}
}
public class MyOtherFunc implements Func1<ObjectA, Observable<ObjectA>> {
@Override
public Observable<ObjectA> call(final ObjectA object) {
//The logic that was formerly in an anonymous Func1 is now here
if (object.isFull()) {
//do something
return myRepository.doThingA();
} else {
//do something else
return myRepository.doThingB();
}
}
}
现在,MyFunc和MyOtherFunc现在可以单独测试,我可以单独验证逻辑是否正确。然后我的原始代码可以转换成这样的东西:
Observable<ObjectA> observable = myRepository
.fetchA()
.flatMap(myFunc)
.flatMap(myOtherFunc);
这肯定看起来更简洁,减轻了我对链条进行单元测试以处理许多案例的需要,但现在的问题是如何测试剩下的内容?
如果我模拟了myFunc和myOtherFunc的输出,那么我似乎根本没有测试任何东西。另一个选择是使用myFunc和myOtherFunc的实际实现,但现在我不再进行单元测试了我正在进行集成测试,而且还有额外的麻烦。
之前有没有其他人在努力解决这个问题,如果是这样,你是如何解决可测性问题的?
答案 0 :(得分:0)
您的方法似乎是合理的,特别是如果您想将被测单元分成他们自己的类。但只要保留代码就可以了,只要它不比你拥有的更复杂。
但如果class ClassUnderTest {
private MyRepository myRepository;
public void doSomething() {
Observable<ObjectA> observable = myRepository
.fetchA()
.flatMap(new Func1<ObjectA, Observable<ObjectA>>() {
@Override
public Observable<ObjectA> call(final ObjectA object) {
return getMoreThings(object);
}
})
.flatMap(new Func1<ObjectA, Observable<ObjectA>>() {
@Override
public Observable<ObjectA> call(final ObjectA object) {
return doThings(object);
}
});
}
Observable<ObjectA> doThings(ObjectA object) {
if (object.isFull()) {
//do something
return myRepository.doThingA();
} else {
//do something else
return myRepository.doThingB();
}
}
Observable<ObjectA> getMoreThings(ObjectA object) {
if (object.isAvailable()) {
//do something
return myRepository.fetchAnotherThing();
} else {
//do something else
return myRepository.fetchADifferentThing();
}
}
内部的逻辑变得越来越复杂,您可以考虑将该代码移到单独的方法中,如下所示:
class ClassUnderTest2 {
private MyRepository myRepository;
public void doSomething() {
Observable<ObjectA> fetchAsync = myRepository.fetchA();
Observable<ObjectA> fetchMoreAsync = getFetchMoreAsync(fetchAsync);
Observable<ObjectA> doThingsAsync = getDoThingsAsync(fetchMoreAsync);
}
Observable<ObjectA> getFetchMoreAsync(Observable<ObjectA> fetchAsync) {
return fetchAsync
.flatMap(new Func1<ObjectA, Observable<ObjectA>>() {
@Override
public Observable<ObjectA> call(final ObjectA object) {
if (object.isAvailable()) {
//do something
return myRepository.fetchAnotherThing();
} else {
//do something else
return myRepository.fetchADifferentThing();
}
}
});
}
Observable<ObjectA> getDoThingsAsync(Observable<ObjectA> fetchMoreAsync) {
return fetchMoreAsync
.flatMap(new Func1<ObjectA, Observable<ObjectA>>() {
@Override
public Observable<ObjectA> call(final ObjectA object) {
if (object.isFull()) {
//do something
return myRepository.doThingA();
} else {
//do something else
return myRepository.doThingB();
}
}
});
}
}
或者你可以用这种方式链接observable:
SELECT *
FROM table_name
ORDER BY Text ASC
在这两个示例中,您将能够独立于完整的Observable流测试行为。