「適切な」方法でユニットテストを行う場合、つまり、すべてのパブリックコールをスタブ化し、プリセット値またはモックを返す場合、実際には何もテストしていないように感じます。文字通り、コードを見て、パブリックメソッドを通るロジックのフローに基づいて例を作成しています。
これは、テストするメソッドが他のいくつかのクラスインスタンス(モックする必要がある)を必要とし、それ自体でいくつかのメソッドを呼び出すように聞こえます。
このタイプのコードは、あなたが概説した理由により、実際にユニットテストするのは困難です。
私が役立ったのは、そのようなクラスを次のように分割することです。
- 実際の「ビジネスロジック」を持つクラス。これらは、他のクラスへの呼び出しをほとんどまたはまったく使用せず、テストが容易です(値の入力-値の出力)。
- 外部システム(ファイル、データベースなど)とインターフェースするクラス。これらは外部システムをラップし、ニーズに合った便利なインターフェースを提供します。
- 「すべてを結びつける」クラス
その後、1のクラスは、値を受け入れて結果を返すだけなので、ユニットテストが簡単です。より複雑な場合、これらのクラスは独自に呼び出しを実行する必要がありますが、2からのみクラスを呼び出します(データベース関数などを直接呼び出しません)。2からのクラスはモックが簡単です(なぜならラップされたシステムの必要な部分を公開します)。
2.と3.のクラスは、通常、意味のある単体テストを行うことはできません(独自に有用なことは何もしないため、単に「接着」コードです)。OTOH、これらのクラスは比較的単純(および少数)になる傾向があるため、統合テストで適切にカバーする必要があります。
例
ワンクラス
データベースから価格を取得し、割引を適用してからデータベースを更新するクラスがあるとします。
これをすべて1つのクラスに含める場合は、模擬するのが難しいDB関数を呼び出す必要があります。擬似コードで:
1 select price from database
2 perform price calculation, possibly fetching parameters from database
3 update price in database
3つのステップすべてにDBアクセスが必要になるため、多くの(複雑な)モックが必要になります。これは、コードまたはDB構造が変更されると壊れる可能性があります。
分割する
3つのクラスに分割します:PriceCalculation、PriceRepository、App。
PriceCalculationは実際の計算のみを行い、必要な値を提供します。アプリはすべてを結び付けます:
App:
fetch price data from PriceRepository
call PriceCalculation with input values
call PriceRepository to update prices
その方法:
- PriceCalculationは、「ビジネスロジック」をカプセル化します。それ自体では何も呼び出さないため、テストは簡単です。
- PriceRepositoryは、模擬データベースを設定し、読み取り呼び出しと更新呼び出しをテストすることにより、疑似ユニットテストを行うことができます。ロジックが少ないため、コードパスが少ないため、これらのテストをあまり必要としません。
- アプリはグルーコードであるため、意味のある単体テストはできません。ただし、これも非常に単純なので、統合テストで十分です。後でアプリが複雑になりすぎると、より多くの「ビジネスロジック」クラスが発生します。
最後に、PriceCalculationが独自のデータベース呼び出しを行う必要があることが判明する場合があります。たとえば、PriceCalculationだけがどのデータが必要かを知っているため、Appによって事前に取得することはできません。次に、PriceCalculationのニーズに合わせてカスタマイズされたPriceRepository(または他のリポジトリクラス)のインスタンスを渡すことができます。PriceRepositoryのインターフェースがシンプルであるため、例えば、このクラスは、嘲笑する必要がありますが、これはシンプルになりますPriceRepository.getPrice(articleNo, contractType)
。最も重要なことは、PriceRepositoryのインターフェースがPriceCalculationをデータベースから隔離しているため、DBスキーマまたはデータ編成の変更がインターフェースを変更する可能性が低いため、モックが破られる可能性が低いことです。