クラスをユニットテストしましたが、統合テストを開始する方法を教えてください。


19

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

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

MCAPIオブジェクトをMailChimpRecipientオブジェクトのコンストラクターに渡すため、PHPUnitを使用して、独自のクラスのすべてのロジックをテストする単体テストを作成しました(MCAPIクラスはテストしていません)。コードカバレッジは100%で、すべてのテストに合格しています。これは、MCAPIオブジェクトのモックとスタブ化によって行われます。

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

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

public function testAddedRecipientCanBeFound()
{
    $emailAddress = 'fred@fredsdomain.com';
    $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クラスはインターフェイスを実装します。これは他のクラスによっても実装されます。アイデアは、ファクトリを使用して、さまざまなタイプのメーリングリスト受信者オブジェクトをコードに渡すことです。これらのオブジェクトはすべて、異なるメーリングリストプロバイダを使用しても同じことを行います。私の統合テストはそのインターフェースをテストするので、インターフェースを実装するすべてのクラスに使用するのはどうですか?その後、将来、交換可能に使用される新しいクラスを設計する場合、プロジェクトに挿入する前に同じ統合テストを実行できます。

これは理にかなっていますか?単体テストはオブジェクトの内部をテストし、統合テストは広告されたとおりに動作することを確認しますか?


4
テストのロジックが多すぎると思います。アサートするまで多くのコードを実行します。おそらく、最初に受信者の削除をテストする必要があります。しかし、それはあなたの質問に答えているのではなく、単なるコメントです。
-hakre

1
さて、setUp関数を使用してテストを実行する根拠を確立する必要があります。入力が未定義の場合、実際にテストすることはできません。入力は正確かつ厳密で、常に同じである必要があります。テストの前提条件が満たされていない場合は、代わりにテストをスキップします。次に、なぜスキップするのか、さらにテストを追加する必要があるか、setUp正しく行われていないのかを分析します。
-hakre

1
また、それ自体のテスト内にテスト値をハードコーディングしないでください。ただし、それらのクラスメンバーをテスト間で共有(および中央の場所で変更)または使用DataProvider(テストへのパラメーターとして入力を提供する関数)できるようにします。
-hakre

1
テスト機能が動作するすべての意味を入力します。受信者の追加をテストし、まだ存在していないことを確認したい場合は、少なくとも削除が開始された場合に削除をアサートする必要があります。そうしないと、テストの前提条件がテスト可能になりません。
-hakre

1
+1は良い質問ですが、プログラマーへの移行にも投票しました。テスト戦略に関する質問がどこにあるようです
-GordonM

回答:


17

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

  • シナリオテスト
  • 機能テスト
  • 単体テスト

通常、各カテゴリにあるテストの量はピラミッドの形になります。つまり、下部にたくさんのユニットテスト、中央にいくつかの機能テスト、いくつかのシナリオテストがあります。

単体テストでは、テスト対象のクラスが使用するすべてをモックアウトし、それを完全に分離してテストします(これは、テスト中に置き換えることができるように、クラス内でインジェクションを通じてすべての依存関係を取得することを確認することが重要な理由です)

単体テストでは、すべての可能性、つまり「ハッピーパス」だけでなく、すべてのエラー状態もテストします。

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

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

車全体を組み立てて、ドライバーとして考えられるすべての状態をチェックすることもできますが、それは本当に難しいことです。

代わりに、すべての可能性を使用してエンジンの小さな部分をテストします(ユニットテスト)

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

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

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

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

  • 単体テストで条件付きロジックを使用しないでください。クリーンアップする必要がある場合、何らかのグローバルステートとテストを使用すると、突然互いに影響を与える可能性があります。
  • テストに関係のないデータは指定しないでください。名前または姓を変更すると、テストは失敗しますか?おそらくそれが重要なのは電子メールアドレスだからではなく、テストで明示的に言及しているからではありません。テストデータを構築するビルダーパターンを見て、本当に重要なことを明示してください。

ありがとう、それは私が考えたことの多くを確認します。明確にするために-これは単体テストではありません。オブジェクトを完全に分離してテストし、オブジェクトのコードカバレッジを100%にするユニットテストを既に作成しました。これは、実際のMCAPIオブジェクトを挿入したときに機能することを確認するための統合テストを目的としています。リストに追加された受信者を削除する必要があります-これがすべてのクリーンアップであり、テストが相互に影響しないことを保証するために実装されています。代わりに何を提案しますか?

1
うん!既にユニットテストを行ったことを理解しました。MCAPIオブジェクトは受信者を追跡しますか?それはあなたがしなければならないクリーンアップですか?サードパーティの「問題」である場合、統合テストでできることは何もありません。他方でリストを追跡する場合は、テストが相互に影響を与えないように、グローバルデータ(およびシングルトン)を避けていることを確認する必要があります。完璧な世界では、テストの開始/終了時に物事を整理し、設計上の欠陥を指摘しますが、現実の世界では、常にそれを避けることはできません。
Wouter de Kort

1
シナリオのテストは、おそらくPHPUnitが実際に適しているものではないことを付け加えます。Yuは、Seleniumなどのブラウザーで実行できるツール、またはjMeterなどのブラウザーをシミュレートできるツールを検討する場合があります。
GordonM

みんなありがとう!良いテスト可能なコードを書くことに関しては、学ぶべきことがたくさんあります。私はこの本のコピーを自分で注文しました:amazon.co.uk/…。私がそれを読んだ後に、皆さんの言ったことが少し理にかなっていることを願っています。@Wouter、私は受信者を削除するだけです。なぜなら、テストによって電子メールアドレスがリストに追加されるからです。リストがそのテストの影響を受けないように削除します。

1
@LewisBassett私はPhp開発者ではありませんが、xUnitテストパターン(amazon.com/xUnit-Test-Patterns-Refactoring-Code/dp/0131495054)は間違いなく良い読み物です。また、misko.hevery.com / code-reviewers-guideの記事は非常に興味深いものです。
はWouterデコルト
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.