Effective JavaのItem 18にあるように、スケルトン実装を持っています(詳細な議論はこちら)。これは抽象クラスであり、2つのパブリックメソッドmethodA()およびmethodB()を提供し、サブクラスメソッドを呼び出して、抽象化された方法では定義できない「ギャップを埋める」。
最初に具体的なクラスを作成し、そのための単体テストを作成して開発しました。2番目のクラスが来たとき、共通の動作を抽出し、2番目のクラスに「欠落したギャップ」を実装することができました(もちろん、2番目のサブクラスに対して単体テストが作成されました)。
時間が経ち、今では4つのサブクラスがあり、それぞれが具体的な実装に非常に固有の3つの短いプロテクトメソッドを実装していますが、骨格実装はすべての一般的な作業を行います。
私の問題は、新しい実装を作成するときに、もう一度テストを書くことです。
- サブクラスは特定の必須メソッドを呼び出しますか?
- 特定のメソッドに特定の注釈がありますか?
- メソッドAから期待される結果が得られますか?
- メソッドBから期待される結果が得られますか?
このアプローチの利点を見ながら:
- テストを通じてサブクラスの要件を文書化します
- 問題のあるリファクタリングの場合、高速で失敗する可能性があります
- サブクラス全体とパブリックAPIを使用してテストします(「methodA()は機能しますか?」ではなく、「このprotectedメソッドは機能しますか?」)
私が持っている問題は、新しいサブクラスのテストは基本的に簡単です:-テストはすべてのサブクラスですべて同じであり、メソッドの戻り値の型を変更するだけです(スケルトン実装はジェネリックです)。テストのアサート部分を確認してください。-通常、サブクラスにはそれらに固有のテストはありません
テストがサブクラス自体の結果に焦点を当て、リファクタリングから保護する方法が好きですが、テストの実装が単なる「手動作業」になったという事実は、私が何か間違ったことをしていると思うようにします。
クラス階層をテストするときにこの問題は一般的ですか?どうすればそれを回避できますか?
ps:スケルトンクラスのテストについて考えましたが、抽象クラスのモックを作成してテストできるようにするのも奇妙に思えます。そして、サブクラスが予期していない動作を変更する抽象クラスのリファクタリングは、それほど速くは気付かれないと思います。私の勇気は、サブクラスを「全体として」テストすることがここで好ましいアプローチであることを教えてくれますが、間違っていると教えてください:)
ps2:私はグーグルで調べて、このテーマに関する多くの質問を見つけました。そのうちの一つがこれ一つであり偉大な答えナイジェルソーンからの、私の場合は彼の「ナンバー1」になります それは素晴らしいことですが、この時点ではリファクタリングできないので、この問題に耐えなければなりません。リファクタリングできれば、各サブクラスを戦略として使用できます。それは問題ありませんが、「メインクラス」と戦略をテストしますが、メインクラスと戦略の統合を壊すリファクタリングは見当たりません。
ps3:抽象クラスをテストすることは「受け入れられる」という回答をいくつか見つけました。これは受け入れられることに同意しますが、この場合にどちらが好ましいアプローチであるかを知りたいと思います(私はまだ単体テストから始めています)