web-dev-qa-db-ja.com

サブクラスまたは抽象親クラスを単体テストする必要がありますか?

効果的なJava(拡張ディスカッション here )の項目18にあるように、私は骨格実装を持っています。これは、2つのパブリックメソッドmethodA()とmethodBを提供する抽象クラスです()サブクラスのメソッドを呼び出して、抽象化された方法では定義できない「ギャップを埋める」。

まず、具象クラスを作成し、そのための単体テストを作成して開発しました。 2番目のクラスが来たとき、私は一般的な動作を抽出し、2番目のクラスに「欠落しているギャップ」を実装することができ、準備が整いました(もちろん、2番目のサブクラスのユニットテストが作成されました)。

時が経ち、今では4つのサブクラスがあり、それぞれが具体的な実装に非常に固有の3つの短い保護されたメソッドを実装していますが、スケルトン実装はすべての一般的な作業を行います。

私の問題は、新しい実装を作成するときに、テストを繰り返し作成することです。

  • サブクラスは特定の必要なメソッドを呼び出しますか?
  • 特定のメソッドには特定の注釈がありますか?
  • メソッドAから期待どおりの結果が得られますか?
  • メソッドBから期待どおりの結果が得られますか?

このアプローチの利点は次のとおりです。

  • テストを通じてサブクラスの要件を文書化します
  • 問題のあるリファクタリングの場合、高速に失敗する可能性があります
  • サブクラス全体をそれらのパブリックAPIを介してテストします(「methodA()は機能しますか?」ではなく、「この保護されたメソッドは機能しますか?」)

私が抱えている問題は、新しいサブクラスのテストは基本的に非常に簡単であることです。-テストはすべてのサブクラスですべて同じで、メソッドの戻り値の型(スケルトン実装は汎用)とプロパティIを変更するだけです。テストのアサート部分を確認します。 -通常、サブクラスにはそれらに固有のテストはありません

テストがサブクラス自体の結果に焦点を当て、それをリファクタリングから保護する方法が好きですが、テストの実装が単なる「手動作業」になったため、何か間違っていると思います。

この問題は、クラス階層をテストするときによく起こりますか?どうすればそれを回避できますか?

ps:スケルトンクラスのテストについて考えましたが、抽象クラスのモックを作成してテストできるようにするのも奇妙に思われます。また、サブクラスで予期されない動作を変更する抽象クラスのリファクタリングは、それほど速くは認識されないと思います。ここでは、サブクラスを「全体として」テストすることをお勧めしますが、私が間違っていることを教えてください:)

ps2:私はググってみて、主題について多くの質問を見つけました。そのうちの1つ これはこれです これには 素晴らしい答え が含まれています。ナイジェルソーンから、私のケースは彼の "ナンバー1"です。それはすばらしいことですが、現時点ではリファクタリングできないため、この問題に対処する必要があります。リファクタリングができれば、各サブクラスを戦略として使用できます。それでも大丈夫ですが、「メインクラス」をテストして戦略をテストしますが、メインクラスと戦略の統合を壊すリファクタリングには気づきませんでした。

ps3:抽象クラスをテストすることは「許容できる」という答えを見つけました。私はこれが受け入れられることに同意しますが、この場合にどちらが望ましいアプローチであるか知りたいです(私はまだユニットテストから始めています)

12
JSBach

abstract基本クラスのテストをどのように記述するかわかりません。それをインスタンス化することはできないため、インスタンスを作成することはできませんto test。テストの目的でのみ、「ダミー」の具象サブクラスを作成することができます-どれだけの使用になるかはわかりません。 YMMV。

新しい実装(クラス)を作成するたびに、その新しい実装を実行するテストを作成する必要があります。機能の一部(「ギャップ」)はeachサブクラス内に実装されるため、これは絶対に必要です。継承された各メソッドの出力は、新しい実装ごとに異なる場合があります(おそらくshould)。

5
Phill W.

通常、基本クラスを作成してインターフェースを適用します。以下の詳細ではなく、そのインターフェースをテストする必要があります。したがって、各クラスを個別にインスタンス化するテストを作成する必要がありますが、基本クラスのメソッドを介してのみテストします。

この方法の利点は、インターフェースをテストしたため、インターフェースの下でリファクタリングできることです。子クラスのコードは親にある必要があります。その逆も同様です。これで、テストにより、コードを移動しても動作が壊れなかったことが証明されます。

一般に、コードの使用方法をテストする必要があります。これは、プライベートメソッドのテストを回避することを意味し、多くの場合、テスト対象のユニットが当初考えていたよりも少し大きくなる可能性があります-おそらく単一のクラスではなく クラスのグループ 目的は、特定のスコープでリファクタリングするときにテストを変更する必要がほとんどないように、変更を分離することです。

4
Michael K