ステップごとに別々のテスト方法を用意するのは良い考えですか?


10

REST APIをテストしています。JSON構造を返すとしましょう。サーバーをテストするための最良のアプローチは何ですか?各テストステップは、前のすべてが成功した場合にのみ成功します。

構造A:すべてを一度にテストする

- Test method 1:
    - make server request
    - assert http response code was 200
    - assert returned file is not empty
    - assert returned file has valid JSON syntax
    - assert returned JSON contains key X

これが最善の解決策のようです。

利点:

  • 1つのサーバー要求のみ
  • 「サーバーはキーXのJSONを返しますか?」という動作全体をテストしています。

構造B:各テストにアサートを徐々に追加します

 - Test method 1:
     - make server request
     - assert http response code was 200
 - Test method 2:
     - make server request
     - assert returned file is not empty
 - Test method 3:
     - make server request
     - assert returned file has valid JSON syntax
 - Test method 4:
     - make server request
     - assert returned JSON contains key X

これが私がそれを始めた方法であり、すべての方法が単一のものだけをテストし、これがより良い分離を生み出すので、これが進むべき道であると確信しました。しかし、今は、これらは単体テストではないので、私の分離は適切ではなく、動作全体をテストする必要があると思います。

構造C:リクエストを1回作成し、キャッシュされた応答に対して個別のテストメソッドを実行する

- make server request and cache it (allow read-only access)

 - Test method 1:
     - assert http response code was 200 on cached server request
 - Test method 2:
     - assert returned file is not empty on cached server request
 - Test method 3:
     - assert returned file has valid JSON syntax on cached server request
 - Test method 4:
     - assert returned JSON contains key X on cached server request

利点:

  • 繰り返しの(高価な)サーバー要求はありません
  • シングルアサートテストメソッドはまだあります

使用するのに最も賢明なテスト構造はどれですか?


その後、既存の回答を無効にするような方法で質問を変更しないでください。ありがとう。
Doc Brown

ご不便をおかけして申し訳ありませんが、それ以外の場合を提案しますか?
mrplow 2016

まず、本当にそのような方法で質問を変更する必要があるかどうか、よく考えます。一部の回答を無効にする何かを追加する必要があると本当に思う場合は、すべての作成者に回答の下にコメントを残して、変更またはテキストに何かを追加するかどうかを尋ねるコメントを残すことができます。
Doc Brown、

2
実際、質問が変更された場合、回答者に通知されると想定していました。これが、トピックのない発言を含むコメントをスパムしたくなかった理由です。将来は著者に通知します。そして、私の質問に答えてくれてありがとう。
mrplow 2016

回答:


3

ベストプラクティスには常に目的があり、その背後には理由があります。特にこれらのベストプラクティスに従う方法と難しさを決定しようとする場合は特に、設計でこれらの理由を検討することをお勧めします。

この場合、すべてのテストテストを単一のものにする主な理由は、最初のテストが失敗した場合、2番目のテストはテストされないということです。あまりにも多くのオピニオンメーカーは、すべてを可能な限り最小のビットに分割し、すべてのビットを可能な限り膨らませることにメリットがあると考えているため、すべてのテストに単一のアサートを含める必要があるという考えが生まれました。

これを盲目的にフォローしないでください。すべてのテストで1つのことをテストする必要がある場合でも、各「もの」の大きさを決定する際にはある程度の考慮が必要です。そのためには、すべてのテストで1つのことをテストする理由を念頭に置く必要があります。最初のバグは2番目のバグを未テストのままにするものではありません。

それで、あなたはあなた自身に尋ねる必要があります-「私は本当にここでこの保証が必要ですか?」

最初のテストケースにバグがあるとしましょう-HTTPレスポンスコードはそうではありません200。したがって、コードのハッキングを開始し、必要な応答コードを取得できなかった理由を理解して、問題を修正します。そして今何?

  • 手動でテストを再度実行する場合は、修正によって問題が解決されたことを確認するために、最初の失敗によって隠された他の問題に遭遇する必要があります。
  • 手動で実行せず(時間がかかりすぎるためでしょうか?)、自動テストサーバーがすべてを実行するのを待って修正をプッシュするだけの場合は、さまざまなアサーションをさまざまなテストに配置できます。この場合のサイクルは非常に長いため、各サイクルでできるだけ多くのバグを発見するように努力することは価値があります。

考慮すべき事項がいくつかあります。

アサーションの依存関係

あなたが説明したテストは単なる例であり、実際のテストはおそらくもっと複雑です-したがって、私がこれから述べることは、実際のテストではそれほど強力ではないかもしれませんが、それでもいくぶん効果的かもしれないので、あなたはそれを検討したいかもしれません。

JSON形式で応答を返すRESTサービス(またはその他のHTTPプロトコル)がある場合、通常は、通常のオブジェクトを返す通常のメソッドのようにRESTメソッドを使用できるようにする単純なクライアントクラスを記述します。クライアントが機能することを確認する個別のテストがあると仮定すると、最初の3つのアサートを破棄し、4つだけを保持します。

どうして?

  • 最初のアサートは冗長です。HTTP応答コードが200でない場合、クライアントクラスは例外をスローする必要があります。
  • 2番目のアサートは冗長です。応答が空の場合、結果オブジェクトはnullまたは空のオブジェクトのその他の表現となり、キーXを配置する場所がなくなります。
  • 3番目のアサートは冗長です。JSONが無効な場合、解析しようとすると例外が発生します。

したがって、これらすべてのテストを実行する必要はありません。4番目のテストを実行するだけで、最初の3つのバグが検出を試みた場合、実際のアサートを取得する前に適切な例外でテストが失敗します。

どのようにレポートを受け取りますか?

テストサーバーからメールを受信せず、代わりにQA部門がテストを実行し、失敗したテストを通知するとします。

QAのジャックがドアをノックします。彼は、最初のテストメソッドが失敗し、RESTメソッドが不正な応答コードを返したと言います。あなたは彼に感謝し、根本的な原因を探し始めます。

次に、QAからJenが来て、3番目のテストメソッドが失敗したと言います。RESTメソッドは、応答本文で有効なJSONを返しませんでした。あなたは既にそのメソッドを見ていると彼女に伝え、あなたはそれが悪い終了コードを返す原因となったのと同じことが、有効なJSONではない何かを返す原因にもなり、例外スタックトレースのように見えたと信じています。

作業に戻りますが、QAのジムが到着し、4番目のテストメソッドが失敗し、応答にXキーがないことがわかりました...

コンピュータの画面がないとコードを見るのが難しいので、理由を探すことすらできません。ジムが十分に速かった場合、彼は時間内に回避できた可能性があります...

テストサーバーからのメールが却下しやすくなりますが、それでも-あなたはというと、単に通知されませんONCE何かが試験方法に問題があること、および関連するテストを見て、自分自身を記録しますか?


3

同じパラメーターを使用してサーバー要求を行うと常に同じように動作すると安全に想定できる場合、メソッドBはほとんど無意味です。1回の呼び出しで十分なときに、同じメソッドを4回呼び出して同じ応答データを4回取得する必要があるのはなぜですか。

これを安全に想定できず、テストの一部にしたい場合は、テストAを複数回実行する方がよいでしょう。

テストフレームワークで明示的なテストメソッドのみのオンとオフの切り替えが許可されており、テストの個々のステップでこれを実行する必要があると予想される場合、Bがメリットをもたらす可能性のある唯一の架空の状況です。

代替Cは、Aと上記のBの利点の1つを組み合わせたようです。テストフレームワークでコードをそのように簡単に構成でき、Bを超えるオーバーヘッドがほとんどない場合、これは実現可能なアプローチです。ただし、これによりAがさらに複雑になるため、個々のテストのオンとオフを切り替える場合にのみ使用します。そうでない場合は、YAGNIの原則を適用して、最も単純なソリューション(A)に固執します。

TLDR:常に1つのテストですべてのアサートを実行することが確実である場合はAで開始し、個々のアサートを外部から簡単に制御する必要がある場合はCにリファクタリングします。


0

他のコードと同様に、時期尚早の最適化は避けてください。最初にテストを作成して、テストが読みやすく、保守が簡単になるようにします。テストが遅くなり始めたら、最適化します。かなり単純な例では、AとBはどちらも読みやすく、保守も容易になるので、物事が遅くなりすぎる(構造B)か、複雑になりすぎる(構造A)まで、どれを選んでもかまいません。

サーバーがステートレスの場合は、実際の応答と予想される応答を比較して、メッセージ全体を一度に確認することで最適化できます。明らかに、それは読みやすさを犠牲にするでしょう。

サーバーがステートフルで、サーバーをテスト用の状態にするために複数の遅いAPI呼び出しを行う必要がある場合、別のアプローチを取るか、テストの実行に数分かかる場合があります。たとえば、データベースの更新を実行してテストデータベースにデータを挿入すると、テストに適した状態のオブジェクトをすばやく取得できます。テストは迅速で読みやすいですが、維持するのがより困難です。または、APIの前にファサードを記述して、複数のAPI呼び出しが単一のAPI呼び出しになり、テストしているビジネスプロセスにより厳密に一致するようにすることもできます。


0

テストは物事を共有すべきではありません-最初からテストを別のテストへの影響を避けます。これにより、ランダムな順序でテストを実行することもできます。
したがって、Cウェイは受け入れられません。


コードを書くとき(またはおそらく何か他のものを作成するとき)は、常に自分自身に尋ねてください。「なぜそのような習慣があるのですか?」
すべてに対して異なるテストがあるべきだと私たちが言うのはなぜですか?

これが必要なケースは2つあります。

  1. 「各テストステップは、以前のすべてが成功した場合にのみ成功する」に依存できない場合
  2. テストに説明的なアサートメッセージがない場合

これらのケースに直面する理由は2つあります。

  1. 「各テストステップは、以前のすべてが成功した場合にのみ成功します」は、製品機能に実際には適用できません。
  2. 経験や時間が足りない、または圧倒的な製品の複雑さのために、製品について十分な知識がない

何らかの理由でこれらの理由の少なくとも1つを宣言できない場合は、やみくもに構造Bを取得します。


それ以外の場合(ここにアクセスしてください)、Aを選択します。


また、Software Quality Assurance&Testing Stackexchangeサイトでこの質問をすることもできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.