2017-08-02 74 views
0

我在如何測試複雜邏輯的RxJava流中有很多操作符。我發現如果我有複雜的邏輯和許多將複雜邏輯串聯在一起的操作符,那麼測試流的單元就會變成一場噩夢,因爲要覆蓋的案例太多了。如何在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

如果爲'myFunc'和'myOtherFunc'創建模擬,則可以測試a)時序,b)錯誤條件,c)並行性,d)組合的變化。例如,當'fetchA()'發出多個項目時會發生什麼?沒有相關產品? –

回答

0

你的方法似乎是合理的,特別是如果你想將被測單元分成他們自己的類。但只要離開代碼就可以了,只要它不比你擁有的更復雜。

但是,如果內部的邏輯你flatMap的也變得越來越複雜,你可能會考慮這些代碼轉移到單獨的方法,這樣的:

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流的行爲。