web-dev-qa-db-ja.com

抽象クラスの動作をテストする

現在、TDDなしで作成された既存のデザインをリファクタリングしています。 1つの抽象基本クラスと2つのサブクラスを持つクラス階層があります。元の設計では、これらのクラスはほとんど動作を持たない単なるデータホルダーでした。基本クラスに実装する必要があるいくつかの機能を特定しました。この機能のテストを今すぐ作成します。しかし、クラスは抽象であるため、インスタンス化できません(明らかに)。

注:テストしたい機能は、pure virtualメソッドを呼び出しません。

class Base {}; // Is abstract

TEST(BaseTest, doesSomethingAmazing) {
    Base aBase; // <-------- Not possible!!! 
    ASSERT_THAT(aBase.amazeMe(), Eq(AMAZING_RESULT));
}

編集:いくつかのことを明確にするために:

  • 継承は実際にこの状況で意味をなします-両方のサブクラスは特定のドメインの概念にマップし、ポリモーフィズムは周囲のコードをクリーンに保つのに役立ちます
  • 両方のサブクラスで使用され、両方のクラスに共通のデータを必要とする動作があります。ですから、一般的なスーパークラスに入れるのは理にかなっていると思います。

私はいくつかの可能な解決策を考えることができますが、それらのどれも私にとって最適ではないようです:

  • すべてのpure virtual関数を実装するサブクラスをテストコードに追加します。欠点:サブクラスに簡潔な名前を付けるのは難しく、テストの理解が難しくなります。
  • 代わりにサブクラスのオブジェクトをインスタンス化してください。欠点:テストをかなり混乱させる
  • 空の実装を基本クラスに追加します。欠点:クラスはもはや抽象的ではない

私はテストをできるだけ明確にするためにオプション3を選ぶ傾向がありますが、私はそれで本当に満足していません。私が知らないもっと良い方法はありますか?

3
lethal-guitar

テストする機能が純粋な仮想メソッドを呼び出さず、サブクラスでオーバーライドされない限り、箇条書きの番号1を使用して、テスト固有のサブクラスを作成します。 Sub<ClassName>またはサブクラス自体が重要ではないことを反映する一般的な名前。

テストしたいものが最終的な具象クラスによって何らかの方法で変更できる場合は、これを検討する必要があるかもしれません。

使用しているC++テストフレームワークに同等のものがあるかどうかはわかりませんが、 NUnit (およびJUnit)では Abstract Test パターンを実装できます。

要するに、あなたは持っています

  • 抽象基本テストクラス。テスト対象オブジェクトの生成に使用される抽象ファクトリメソッドと、テストメソッド自体が含まれています。これらのメソッドは、その下にある具体的なオブジェクトに関係なく、真でなければならないことを検証します。

  • テスト中の抽象クラスの派生物と同じ数の派生テストクラス。ここでファクトリメソッドを実装して、派生クラスのインスタンスを返すだけです。

テストランナーは、派生テストクラスを確認すると、ファクトリメソッドで生成されたオブジェクトに対して、基本テストクラスから継承したすべてのテストメソッドを自動的に実行します。

2
guillaume31

次のことができます。

  • 純粋な仮想メソッドのみを含む抽象基本クラスを作成します(例:AmazingObjectInterface)。
  • テストする必要がある非仮想メソッドを定義するBasicAmazingObjectというサブクラスを追加します。
  • 最後に、元の2つのサブクラスがBasicAmazingObjectのサブクラスになります。

これにより、変化するものを分離し、必要なものを適切にテストできます。もちろん、yoはもう1つのクラスを導入しますが、これはC++での(Javaの意味での)インターフェースサポートの欠如に対する代償)です。

1
sansuiso