web-dev-qa-db-ja.com

クラスを単体テストしましたが、統合テストを開始するにはどうすればよいですか?

MailChimpRecipientという、MailChimpリストの受信者を管理するクラスを作成しました。サードパーティのAPIラッパーであるMCAPIクラスを使用します。

http://apidocs.mailchimp.com/api/1.3/ http://apidocs.mailchimp.com/api/downloads/

MCAPIオブジェクトをMailChimpRecipientオブジェクトのコンストラクターに渡しているので、PHPUnitを使用してユニットテストを記述し、自分のクラスのすべてのロジックをテストしています(MCAPIクラスはテストしていません)。コードカバレッジは100%で、すべてのテストに合格しています。これは、MCAPIオブジェクトをモックおよびスタブすることによって行われます。

次のステップは、PHPUnitを使用して統合テストを作成することでした。実際のMailChimpリストを使用するように設定された実際のMCAPIオブジェクトを使用してMailChimpRecipientフィクスチャを構築します。

私は、基本的にオブジェクトのパブリックインターフェイスに対して再度テストを実行する統合テストだと思うものを書きました。

public function testAddedRecipientCanBeFound()
{
    $emailAddress = '[email protected]';
    $forename = 'Fred';
    $surname = 'Smith';

    // First, delete the email address if it is already on the list
    $oldRecipient = $this->createRecipient();
    if($oldRecipient->find($emailAddress))
    {
        $oldRecipient->delete();
    }
    unset($oldRecipient);

    // Add the recipient using the test data
    $newRecipient = $this->createRecipient();
    $newRecipient->setForename($forename);
    $newRecipient->setSurname($surname);
    $newRecipient->setEmailAddress($emailAddress);
    $newRecipient->add();
    unset($newRecipient);

    // Assert that the recipient can be found using the same email address
    $this->assertTrue($this->_recipient->find($emailAddress));
}

「統合」テストでは、クラスの内部はテストされません。実際のMCAPIオブジェクトが指定されている場合、それが宣伝どおりに動作することを確認するだけです。

これは正しいです?これは統合テストを実行する最良の方法ですか?結局のところ、内部は単体テストでテストされています。動作が宣伝されている方法に従って、統合テストが実際に機能するかどうかをテストするためにそこにあると私は思っていますか?

さらに一歩進むために、MailChimpRecipientクラスは、他のクラスによっても実装されるインターフェースを実装します。アイデアは、異なるメーリングリストプロバイダーを使用しているにもかかわらず、ファクトリを使用して、さまざまな種類のメーリングリスト受信者オブジェクトをコードに渡すことです。私の統合テストはそのインターフェースをテストするので、インターフェースを実装するすべてのクラスにそれを使用するのはどうですか?そうすれば、将来、互換性のある新しいクラスを設計すれば、プロジェクトに挿入する前に同じ統合テストを実行できます。

これは合理的に聞こえますか?単体テストはオブジェクトの内部をテストし、統合テストはそれが宣伝どおりに動作することを確認しますか?

19
Lewis Bassett

コードをテストするときは、次の3つの領域に注意を払う必要があります。

  • シナリオテスト
  • 機能テスト
  • ユニットテスト

通常、各カテゴリで行うテストの量はピラミッドの形になります。つまり、下部に多数の単体テストがあり、中央にいくつかの機能テストがあり、シナリオテストがいくつかあります。

単体テストでは、テスト中のクラスが使用するすべてのものを模擬し、純粋に分離してテストします(これが、クラス内で、注入を通じてすべての依存関係を取得して、テスト中に置き換えられるようにすることが重要である理由です)。

単体テストでは、すべての可能性をテストするため、「ハッピーパス」だけでなく、すべてのエラー条件もテストします。

すべてのユニットが独立して動作することが完全に確実である場合は、いくつかのテスト(機能テスト)を作成して、ユニットが結合されたときにも動作することを確認します。次に、すべての機能モジュール間の配線をテストするシナリオテストを記述します。

たとえば、自動車をテストしているとします。

あなたは車全体を組み立て、ドライバーとしてあらゆる可能な状態をチェックすることができますが、それは本当に難しいでしょう。

代わりに、すべての可能性でエンジンの小さな部分をテストします(単体テスト)

次に、機能テストとなるエンジン全体(車とは別)をテストします。

最後のテストとして、キーを入れて車を始動し、駐車場まで運転します。それが機能していれば、すべての部品(バッテリー、燃料、エンジンなど)が接続されていることがわかり、それらを個別にテストしたので、車全体が正しく機能していることを確認できます。

したがって、あなたのケースでは、ユニットテストですべてのエラー条件とハッピーパスをテストし、配線が正しいかどうかを確認するために「実際のコンポーネント」を使用したエンドツーエンドのテストのみが必要であることを知っています。

他のいくつかのポイント、

  • 単体テストでは条件付きロジックを使用しないでください。クリーンアップする必要がある場合、ある種のグローバル状態とテストを使用すると、突然互いに影響し合う可能性があります。
  • テストに関係のないデータは指定しないでください。姓名を変更すると、テストは失敗しますか?重要なのはメールアドレスだからではないでしょうが、テストで明示的に言及しているため、確信が持てません。テストデータを作成するためのビルダーパターンを調べ、本当に重要なことを明示してみてください。
17
Wouter de Kort