2017-08-02 4 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つのテストメソッドを記述する必要があります条件付きロジック。

通常、このようなことが起きたときは、論理を処理してユニットテストを行うカスタムクラスを作成します。今MyFuncととMyOtherFuncは自分で今ユニットテスト可能であり、私はロジックが孤立して正しいことを確認することができます

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

:だから、上記のコードは次のようなものになるかもしれません。その後、私の元のコードは次のように変換することができます:確かに、より簡潔になりますと、それは以前と同様に、多くのケースを処理するためにチェーンの私のユニットテストの必要性を軽減、今の質問はどのようにある

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ストリーム全体とは関係なく動作をテストできます。