抽象クラス、および抽象クラスを拡張するクラスを単体テストする方法を知りたいです。
抽象クラスを拡張して抽象メソッドをスタブ化してテストし、次にすべての具象メソッドをテストする必要がありますか? 次に、オーバーライドするメソッドのみをテストし、抽象クラスを拡張するオブジェクトの単体テストで抽象メソッドをテストしますか?
抽象クラスのメソッドをテストするために使用できる抽象テスト ケースを用意し、抽象クラスを拡張するオブジェクトのテスト ケースでこのクラスを拡張する必要がありますか?
私の抽象クラスにはいくつかの具体的なメソッドがあることに注意してください。
ベストアンサー1
抽象基本クラスを使用する方法は 2 つあります。
抽象オブジェクトを特殊化していますが、すべてのクライアントは基本インターフェイスを通じて派生クラスを使用します。
抽象基本クラスを使用して設計内のオブジェクト内の重複を排除し、クライアントは独自のインターフェースを通じて具体的な実装を使用します。
解決策 1 - 戦略パターン
最初の状況の場合、派生クラスが実装している抽象クラスの仮想メソッドによって定義されたインターフェースが実際に存在します。
これを実際のインターフェースにすること、抽象クラスを具体的なものに変更すること、そしてコンストラクターでこのインターフェースのインスタンスを取得することを検討する必要があります。すると、派生クラスがこの新しいインターフェースの実装になります。
つまり、新しいインターフェースのモック インスタンスを使用して、以前は抽象だったクラスをテストできるようになり、新しい実装はそれぞれ、現在公開されているインターフェースを通じてテストできるようになりました。すべてがシンプルでテスト可能です。
解決策 2
2 番目の状況の場合、抽象クラスはヘルパー クラスとして機能しています。
含まれる機能を確認してください。操作対象のオブジェクトにプッシュして、重複を最小限に抑えることができるかどうかを確認します。まだ残っている場合は、具体的な実装がコンストラクターで受け取るヘルパー クラスを作成し、基本クラスを削除することを検討してください。
これにより、シンプルで簡単にテストできる具体的なクラスが作成されます。
原則として
複雑なオブジェクトの単純なネットワークよりも、単純なオブジェクトの複雑なネットワークを優先します。
拡張可能でテスト可能なコードの鍵となるのは、小さなビルディング ブロックと独立した配線です。
更新: 両方の混合をどのように処理しますか?
両方の役割を果たす基本クラスを持つことは可能です。つまり、基本クラスにはパブリック インターフェイスがあり、保護されたヘルパー メソッドがあります。この場合、ヘルパー メソッドを 1 つのクラスにまとめ (シナリオ 2)、継承ツリーを戦略パターンに変換できます。
基本クラスが直接実装するメソッドと仮想的なメソッドがある場合でも、継承ツリーを戦略パターンに変換できますが、これは責任が正しく調整されていないことを示す良い指標であり、リファクタリングが必要になる可能性があると見なします。
アップデート 2 : 抽象クラスを足がかりとして (2014/06/12)
先日、abstract を使用する状況があったので、その理由を探ってみたいと思います。
設定ファイルには標準形式があります。この特定のツールには、すべてその形式の 3 つの設定ファイルがあります。依存性注入を通じてクラスが関心のある設定を要求できるように、設定ファイルごとに厳密に型指定されたクラスが必要でした。
私は、設定ファイルの形式を解析する方法を知っている抽象基本クラスと、同じメソッドを公開しながらも設定ファイルの場所をカプセル化した派生クラスを用意することでこれを実装しました。
3 つのクラスがラップする「SettingsFileParser」を作成し、基本クラスに委任してデータ アクセス メソッドを公開することもできました。しかし、他のクラスよりも委任コードが多い 3 つの派生クラスが作成されるため、まだこれを行わないことを選択しました。
しかし、このコードが進化し、これらの各設定クラスの消費者がより明確になるにつれて、各設定ユーザーはいくつかの設定を要求し、何らかの方法でそれらを変換します (設定はテキストであるため、オブジェクトにラップしたり、数値に変換したりする場合があります)。これが発生すると、このロジックをデータ操作メソッドに抽出し、厳密に型指定された設定クラスにプッシュバックします。これにより、各設定セットのより高レベルのインターフェイスが実現し、最終的には「設定」を扱っていることを認識しなくなります。
この時点で、厳密に型指定された設定クラスでは、基礎となる「設定」実装を公開する「getter」メソッドは不要になります。
その時点で、パブリック インターフェイスに設定アクセサー メソッドを含める必要がなくなるため、このクラスを変更して、設定パーサー クラスから派生するのではなく、設定パーサー クラスをカプセル化します。
したがって、抽象クラスは、現時点では委任コードを回避する方法であり、後で設計を変更する必要があることを思い出すためのコード内のマーカーです。これを使用することはないかもしれないので、しばらくは存続するかもしれません...それはコードだけが知ることができます。
これは、「静的メソッドなし」や「プライベート メソッドなし」などのあらゆるルールに当てはまると思います。これらはコードの匂いを示しています...そしてそれは良いことです。見逃していた抽象化を探し続け、その間に顧客に価値を提供し続けることができます。
このようなルールは、保守可能なコードが谷間にあるような地形を定義するものだと私は想像しています。新しい動作を追加すると、コードに雨が降るようなものです。最初は、コードがどこに落ちてもそのまま置きます。その後、リファクタリングして、優れた設計の力で動作を移動させ、最終的に谷間に落ち着くようにします。