web-dev-qa-db-ja.com

内部コンポーネントの単体テスト

クラス/モジュール/パッケージ/などの内部/プライベートコンポーネントをどのくらいユニットテストしますか?それらをまったくテストしますか、それとも単に外界へのインターフェースをテストしますか?これらの内部の例は、プライベートメソッドです。

例として、1つの中央手続きから呼び出されるいくつかの内部手続き(関数/メソッド)がある 再帰降下パーサー を想像してください。外の世界への唯一のインターフェースは、文字列を取り、解析された情報を返す中心的な手順です。他のプロシージャは文字列のさまざまな部分を解析し、中央のプロシージャまたは他のプロシージャから呼び出されます。

当然、サンプル文字列を使用して呼び出し、手作業で解析した出力と比較することにより、外部インターフェイスをテストする必要があります。しかし、他の手順はどうですか?それらを個別にテストして、部分文字列が正しく解析されることを確認しますか?

私はいくつかの議論を考えることができます:

長所

  1. より多くのテストは常に優れており、これはコードカバレッジを増やすのに役立ちます
  2. 一部の内部コンポーネントは、外部インターフェースに入力を与えることにより、特定の入力(例えばEdgeケース)を与えることが難しい場合があります
  3. より明確なテスト。内部コンポーネントに(修正された)バグがある場合、そのコンポーネントのテストケースにより、バグがその特定のコンポーネントにあることが明らかになります

短所

  1. リファクタリングは面倒で時間がかかりすぎます。何かを変更するには、外部インターフェイスのユーザーが影響を受けていない場合でも、ユニットテストを書き直す必要があります。
  2. 一部の言語とテストフレームワークでは許可されていません

あなたの意見は何ですか?

14
imgx64

Case:「モジュール」(広義には、パブリックインターフェースと、場合によってはいくつかのプライベート内部パーツを持つもの)には、内部に複雑な/複雑なロジックがあります。モジュールインターフェースのみをテストすることは、モジュールの内部構造に関連する統合テストのようなものです。したがって、エラーが検出された場合、そのようなテストは、障害の原因となっている正確な内部/コンポーネントを特定しません。

ソリューション:複雑な内部パーツをモジュール自体に変換し、それらを単体テストして(複雑すぎる場合はこれらのステップを繰り返して)、元のモジュールにインポートします。これで、uniit-test(動作が正しいことを確認し、エラーを修正する)を簡単に行うのに十分単純なモジュールのセットが手に入りました。これですべてです。

注:

  • 「サブモジュール」が新しい/変更された契約を満たすのに十分なサービスを提供しない場合を除き、モジュールの契約を変更するときに、モジュールの(以前の)「サブモジュール」のテストで何も変更する必要はありません。

  • 不必要なことは何も行われませんpublicつまり、モジュールのコントラクトが保持され、カプセル化が維持されます。

[更新]

オブジェクトのパブリックインターフェースを介して入力を供給するだけで、オブジェクトの内部パーツ(つまり、プライベートにインポートされたモジュール/パッケージではないメンバー)を適切な状態にすることが難しい場合に、いくつかのスマート内部ロジックをテストするには:

  • フレンド(C++の用語で)またはパッケージ(Java)にアクセスして、内部から実際に状態を設定し、必要に応じて動作をテストするテストコードを用意するだけです。

    • これにより、カプセル化が再度解除されることはありませんが、テスト目的で内部に簡単に直接アクセスできます。テストを「ブラックボックス」として実行し、リリースビルドでコンパイルするだけです。
8
mlvljr

FSMベースのコードへのアプローチは、従来使用されているものとは少し異なります。これは、ハードウェアテスト(通常はFSMでもあります)の説明 here とよく似ています。

つまり、特定の出力を生成するだけでなく、特定の「不良」出力を生成するときに、障害の性質によって障害のあるコンポーネントを特定できるテスト入力シーケンス(または一連のテスト入力シーケンス)を作成します。このアプローチは非常にスケーラブルであり、テスト設計に費やす時間が長いほど、テストの品質が向上します。

この種のテストは、いわゆる「機能テスト」に近いものですが、実装に少し触れるたびにテストを変更する必要がなくなります。

2
bobah

まあ-それは依存します:-)。 BDD(Behaviour Driven Development)またはATDD(Acceptance Test Driven Development)アプローチを使用している場合、パブリックインターフェイスのテストは問題ありません(さまざまな入力で徹底的にテストする限り)。プライベートメソッド(プライベートメソッドなど)は実際に重要です。

ただし、そのアルゴリズムの一部を特定の時間枠内または特定のbigO曲線(nlognなど)に沿って実行したい場合は、個々の部分をテストすることが重要です。これを従来のTDD /ユニットテストアプローチと呼ぶ人もいます。

すべてのように、YMMV

2
Martijn Verburg

ParseQuotedString()ParseExpression()ParseStatement()ParseFile()などの機能的な意味を持つ複数の部分に分割し、それらをすべて公開します。構文が大きく変化し、これらが無関係になる可能性はどのくらいありますか?

1
DomQ