テストの適切な設計について質問があります。
クラスA
があるとします
class A{
public:
virtual int methodA();
virtual int methodB();
virtual int methodC();
virtual int methodD();
};
そしてクラスB
class B: public A{
public:
int methodA();
int methodB();
int methodC();
int methodD();
};
クラスBが1つのメソッド(または少数のメソッド)のみをオーバーライドするとします。
テストはどのように設計する必要がありますか?クラスAのテストフィクスチャとクラスBの個別のテストフィクスチャを作成できます。問題は、多くのテストがクラスAとクラスBで同じになるため、コードが非常に重複していることです(多くのメソッドがオーバーライドされないため、同じ動作になります。)
あなたは間違ったコードに焦点を合わせています。
テストの要点は、AまたはBが想定どおりに動作することを証明することではありません。テストのポイントは、AとBのクライアントが必要なものを取得できるかどうかをテストすることです。
そのため、Cの外観がわからないテストを記述しても、Cはこのテストに合格する必要があります。 A、B、またはCを書く前にテストを書くことができます。これは、A、B、またはCについてではないためです。それは、何を使用するかについてです。それらをどのように使用しても、どのメソッドがオーバーライドされたかは関係ありません。
A、B、およびCは、実装の詳細でいっぱいです。それはテストされているものではありません。テストされているのは、実装に関係なく、クライアントコードの期待が満たされていることです。テストは、それらの期待を文書化して実施するために存在します。
クライアントコードに必要な抽象化に対するテストを記述します。 A、B、またはCが実際に何であるかに対してではありません。
これを正しく行うと、A、B、またはCがテストに失敗した場合、テストを書き直すことはできません。それらを書き直します。
デザインが LiskovのSubstitution Principe(LSP) (SOLIDではLとも呼ばれる)に準拠している場合、class B
がclass A
のテストに合格すると想定できます。次に、テストを次のように構成します。
class A
のテストの最初のバッテリーを作成します。これらのテストは、クラスがその矛盾を満たしているかどうかをチェックする必要があります。つまり、すべてのメソッドについて、いくつかの preconditions を指定すると、期待される post-conditions が満たされ、期待される結果が得られます。 class invariants はtrueのままです。class A
とclass B
はどちらも、LSPに従ってこの一連のテストに合格する必要があります。class B
のセカンドバッテリーテストを行います。これらのテストでは、class B
が、事前条件、事後条件、および不変条件が特殊化されている、より特殊な契約を満たしているかどうかを確認する必要があります。変更されていない動作は、最初の一連のテストですでにテストされているため、特定のテストを必要としません。デザインがLSPに準拠していない場合は、残念ながらclass B
がclass A
のテストに合格するとは限りません。次に、クラスとメソッドが仕様に準拠しているかどうかを検証するために、冗長なテストケースを作成する必要があります。
上記の文言が不明確な場合(コメントとdvを参照)、テストを減らすことはお勧めしませんが、逆に、デザインプロパティを使用してより多くのテストを行うことを強調します(同じ労力またはもっと少なく)。
2番目の箇条書きで表されているアイデアは、クラスBがクラスAによって約束されたすべての契約に対してテストされることを意味します。不十分な設計または実装エラーがLSPに違反している場合、これは最初のラウンドのテストですぐに通知されます(テストケースの失敗)。 。
また、提案された戦略はBの実装の詳細に依存しないことに注意してください。それは、実装方法に関係なく、Bが保証する必要のある契約にのみ依存します。
ここでの主なメッセージは、SOLIDのLは単なる概念的なものではなく、ソフトウェアのテストと保守を容易にする強力なアプローチです。
タイプAのオブジェクト用に作成されたテストはタイプBのオブジェクトでも機能するため、両方のクラスで同様に動作すると予想されるすべてのメソッドのすべてのテストを再利用できます。明らかに、異なる動作が予想されるメソッドに対して新しいテストを作成する必要があります。
両方のクラスのテストフィクスチャは、たとえば、タイプAのオブジェクトによってパラメーター化された共通コードを配置する共通基本クラスから派生できます。代わりに、共通コードのヘルパークラスを使用することもできます。最も適切なのはケース依存であり、コード例の観点から検討する必要があります(これはStackoverflowまたはCodereview.SEで良い質問をする可能性があります)。