テストの概要
公開: 2021-06-16テストに関連するすべてについての新しいブログシリーズへようこそ。 うまくいけば、これらのブログ投稿は、Mediatoolkitでそれらをどのように書くかとその理由についての一般的な考えをあなたに与えるでしょう。 例に飛び込む前に、テストを書くことのいくつかの基本的な定義、アイデア、および利点から始めましょう。
どんな種類のテストがありますか?
すべてのテストが同じように作成されるわけではありません。 さまざまな目的のためにさまざまな種類のテストがあります。 この投稿は主に単体テストに焦点を当てていますが、違いを認識し、それぞれの利点を理解する必要があります。
1.手動テスト
手動テストには、テスターが手動で実行するテストが含まれます。 これらは通常、品質保証または開発者自身によって行われます。 QAは、メインのアプリケーションテスターではなく、最後の防衛線と見なす必要があります(UIテストの主な方法として手動テストを行うQAは、この場合の有効な例外です)。
開発者は通常、ローカルでいくつかのことを実行し、コードをステージングフェーズにプッシュせずにシステムがどのように動作するかを確認したい場合、または神があなたを本番環境から救いたいときに、手動テストを作成します。 ただし、これらのテストの目的は、コードの堅牢性ではありません。 これらのテストの唯一の役割は、バグを見つけて製品の品質を確認することです。
2.統合テスト
システムは複数のコンポーネントで構成されているか、少なくともそうあるべきです。 統合テストは、これらのコンポーネントのパブリックAPIをチェックします。 REST APIだけでなく、Kafkaトピック通信などの公開されているAPI。
コンポーネントが特定の形式でトピックに関するメッセージを出力すると「言う」場合、それはそのパブリックAPIまたはコントラクトです。 統合テストでは、個別に開発された複数のコンポーネントが通信できるかどうか、およびそれらの契約がグループとして一致するかどうかを確認します。
これらのコンポーネントを個別にテストするだけの場合、それらの個々の動作は正しく機能する可能性がありますが、より大きなグループでそれらを接続しようとすると、それらのコントラクトが異なることがわかります。
3.エンドツーエンド(E2E)テスト
エンドツーエンドのテストは、最終製品のユーザーエクスペリエンスフローを確認することです。 今日のシステムの複雑さは、テストでカバーするのは困難です。 多くのシステムは相互に依存しており、E2Eテストにより、製品が期待どおりに動作することが確認されます。 QAは、エンドユーザーフローを調べ、すべてのシステムが期待どおりに動作するかどうかを確認することにより、製品の正確性を検証します。
4.ユニットテスト
単体テストは、信頼できるソフトウェアのバックボーンです。 それらは他のテストの基礎を作ります。 彼らは個々のユニットをテストします。
開発者は、単一の方法でユニットの定義を誤解する可能性があります。 単体テストでは、複数の異なるクラスで構成される可能性のあるコンポーネントの動作をテストする必要があります。 他のコンポーネントからアクセスできるパブリックメソッドをテストする必要があります。保護されたメソッドやクラスはテストしないでください。 これらは実装の詳細であり、パブリックAPIの一部ではありません。
なぜ私たちもテストする必要がありますか?
優れたテストを作成し、頻繁に作成すれば、日の目を見る前に製品の品質を保証できます。
システムが古くなるにつれて、テストの利点がますます明らかになります。 私たちの環境は信頼できるものになり、必然的に問題が発生した場合に開発時間と多くの髪の毛を引っ張ることを節約します。 同僚は、テストを覗いて、手動で実行しなくてもコードが実行すべきことと実行すべきでないことを理解できると、感謝するでしょう。
堅牢なコード
「ロバストコード」について話し始める前に、それをどのように定義する必要がありますか? コードを堅牢にする理由は何ですか? それは、防御的にプログラムし、他の開発者がどのようにコードを悪用する可能性があるかを考える必要があることを意味しますか?
堅牢なコードは常にシンプルでクリーンです。 単純さは堅牢で、複雑さは壊れやすいです。 無効な入力を処理する必要がありますが、それは防御的にプログラムする必要があり、チームを信頼しないという意味ではありません。
ゴールの法則:機能する複雑なシステムは、機能する単純なシステムから進化したものであることが常にわかっています。
逆の命題も当てはまるようです。ゼロから設計された複雑なシステムは機能せず、機能させることもできません。 動作する単純なシステムから始めて、最初からやり直す必要があります。
安全にリファクタリング
コードがテストの対象になると、既存のコードを変更することを恐れなくなります。 変更するたびに、テストを実行して、問題が発生していないことを確認できます。 あなたがテストをするとき、あなたは防御的にプログラムする必要はありません。
テストなしのリファクタリングは、滑りやすい坂を下って行き、眠れない夜と日曜日に働くことになります。 このトピックは広すぎてここで取り上げることができず、将来的には独自のブログ投稿に値します。
どうすればテストを書けないのですか?
ユニットテストを書かない方法を知ることは、それらを書く方法と同じくらい重要です。
- メソッド呼び出しの結果を出力するテストを作成することは、目的の結果を検証しないため、テストではありません。
- テストがDocumentsフォルダー内のファイルからデータを読み取る場合、テストは環境に依存してはならないため、実際の単体テストではありません。
- すべての開発者は、コードをチェックアウトして、他に何もせずにテストを正常に実行できる必要があります。
- すべての単体テストは、他のテストから独立している必要があります。 これは、テストの実行順序も重要ではないことを意味します。
- コードを変更しない場合、テストを複数回実行すると、常に同じ結果で終了するはずです。
- 単体テストでは、個々のメソッド呼び出しではなく、動作をテストする必要があります。 すべてのクラスとメソッドがテストを行う必要があるわけではありません。
動作は、システムのユーザーが必要とする真の価値を生み出すものです。 ユーザーは、 productFactory.create()が2回呼び出されたときに同じオブジェクトを作成したかどうか、またはリポジトリがいくつかのパラメーターを使用して呼び出されたかどうかを知る必要がありますか? おそらくそうではありませんが、それでも多くの開発者はまさにこれらの種類のテストを作成しています。
テストがそのように見える場合、それらは実装と緊密に結合されています。 動作が同じであっても、実装の詳細を変更するたびに、テストを更新する必要があります。 テストは、実装の詳細ではなく、動作が変更された場合にのみ変更する必要があります。 つまり、コードがどのように実行されるかではなく、コードが何を実行するかをテストします。
テストをどのように作成しますか?
私たちのテストは、ベストコードプラクティスに従う必要があり、環境に依存せず、高速に実行する必要があります。
テストの実行時間をできるだけ短くすることが重要です。 各テストには、数ミリ秒以上かかることはありません。 テストの実行に時間がかかりすぎると、人々はテストをスキップし、JenkinsなどのCIサーバーに依存して、デプロイメント実行可能ファイルをビルドできないときに大騒ぎする傾向があります。
各テストは3つの「A」セクションで構成されています(AAAパターン) :
- 整える
- 活動
- 主張する
1.アレンジ
テストのアレンジセクションでは、テストする動作を呼び出す前に、システムが特定の状態にあることを確認します。 「システム」は、動作を生成し、一時ファイルまたはその性質のものを作成するために特定の方法で設定する必要があるオブジェクトである可能性があります。

このセクションのコードは通常、他の2つの組み合わせよりも大きくなります。
このセクションを小さく保つために特に役立つデザインパターンの1つは、 Object Motherです。 このデザインパターンはFactoryと非常に似ていますが、事前に構成されたオブジェクトを作成するためのより具体的なメソッドがあります。 標準のFactoryにはcreateCar(carDescription)などのメソッドがありますが、 ObjectMotherにはcreateRedFerrari() 、 createBlackTesla() )、 createBrokenYugo() ()などのメソッドがあります。
2.行動する
テストのこのセクションには1行が必要です。 この行は、テスト中の動作を実行します。 このセクションに複数の行を書いていることに気付いた場合は、おそらくあなたの行動を正しくカプセル化していないでしょう。 クライアントがオブジェクトの複数のメソッドを特定の順序で呼び出すことを期待すべきではないのに、なぜテストを行うのでしょうか。
この行は、テストするメソッド呼び出しです。 このメソッドが結果を返す場合は、その値を変数に格納して、それがAssertステップで期待される値であるかどうかを確認する必要があります。
3.アサート
アレンジセクションでシステムを準備し、アクトセクションでテスト対象のアクションを実行した後、アクションの結果を検証する必要があります。 通常、ここでメソッドの結果を確認しますが、メソッドが値を返さない場合もありますが、それでも副作用が発生します。 コードがオブジェクトの状態を変更したり、ファイルを作成したり、 Listから何かを削除したりすることが予想される場合は、それが正確に行われたかどうかを確認する必要があります。
スタブとモック
ほとんどの開発者は、モックとスタブという用語を同じ意味で使用していますが、違いがあります。
スタブはテストに失敗できませんが、モックは失敗できます。
スタブは状態ベースであり、ハードコードされた値を返します。 「どのような結果が得られましたか?」
モックは動作ベースです。モックを使用して、動作がどのように通過するかを確認します。 「どうやって結果を得たのですか?」
単体テストにモックが散らばっている場合、テストは非常に壊れやすくなります。つまり、実装の詳細の1つを変更するたびに、すべてのモック呼び出しを更新する必要があります。
1つだけテストします。
1つの動作を分離し、それが機能することを証明できる必要があります。 その動作が異なる入力で異なる動作をする必要がある場合は、それらの動作ごとに新しいテストを作成する必要があります。 一度に複数のことをテストする大規模なテストがある場合、なぜテストが失敗したのかを知るのは困難です。 さらに、不要になった機能を削除したり、新しいコードを追加したときに壊れた機能を確認したりするのが難しくなります。
テスト対象の動作を複数回呼び出す必要がない限り、テストの最後のセクションに複数のアサーションを含めることはまったく問題ありません。 その場合、障害のある動作を特定して修正するのは困難です。
1つのテストで複数の動作をアサートすると、テストは最初の失敗のみを報告し、残りはスキップされるため、正確に機能しないものを明確に把握することはできません。 どのような変更が必要で、どれだけのことが期待どおりに機能しないのかを理解するのは非常に難しくなります。
ネーミングテスト
優れたテストと優れたテストを区別するものの1つは、テスト名です。 テストは、彼らが何をするかだけでなく、いつそれをするかを教えてくれるはずです。
使用できる適切な命名パターンはたくさんあるので、最もわかりやすいものを選び、それを使い続けてください。 優れたテスト名の例を次に示します。
- RegistrationServiceShould.createNewAccountWhenEmailIsNotTaken
- RegistrationServiceTest.whenEmailIsFree_createNewAccount
- RegistrationServiceTest.if_freeEmail_when_userCreatesAccount_then_create
単体テストを作成するときは、メソッドの命名方法のベストに従うよりも、テスト対象を伝えることがはるかに重要です。 たとえば、Javaでは、メソッドを作成するときにキャメルケースを使用しますが、アンダースコア(_)を使用して、テスト名のアクションから状態を分離することは完全に有効です。
クリーンテスト
作成するテストは、コードに適用するすべてのクリーンなコードプラクティスに従う必要があります。 テストは二級市民ではないため、コードを読みやすくするために、残りのコードと同じレベルの注意を払う必要があります。
テストでのコード重複の定義は非常に重要です。 DRYの原則(Do n't Repeat Yourself)は、同じ理由で変化する動作の抽出に適用されます。 テストはさまざまな理由で変更されるため、同じ理由で本当に変更されない場合は、テストから物事を抽出する際にさまざまです。 ネタバレ注意、彼らはしばしばしません。
ステートメントがテストに属していないif 。 ifステートメントは、テストが少なくとも2つの異なることを実行することを示しており、テストを2つの異なるテストとして書き直した方がよいでしょう。 適切に名前を付けると、テストの機能と、すべての異なる動作を理解しやすくなります。
いつテストを書くべきですか?
TDDの原則に基づいて、新しいコードを作成する前にテストを作成する必要があります。
新しい機能を追加する必要がある場合は、最初に目的の動作を新しいテストとして説明します。 他のテストを中断することなく、そのテストに合格するために必要な最小限の変更を行います。
そうしないと、時間が経つほど、テストされていないコードが増え、バグや過剰設計が発生する可能性が高くなります。
また、テストするコードが増えたため、テストはより複雑になります。通常、開発者はテストをコードに合わせて調整します。 つまり、その逆ではなく、コードの動作に一致するように動作を調整します。
早い段階でテストを作成すると、問題の定義が小さくなり、より一般的で複雑な動作よりも、そのような問題に頭を悩ませやすくなります。
最終的な考え
テストを書くことはオプションのように思えるかもしれませんが、良い基礎から始めることが重要です。 コーディングはすでに困難です。読みやすく、理解しやすく、保守しやすいテスト可能なコードを作成することで、自分自身とチームメートが簡単にコーディングできるようにします。 最後に、テストを希望に準拠させるのが難しい場合は、テストよりもコードに問題がある可能性が高くなります。
クリーンでテスト可能なコードの作成に興味がありますか?
シニアバックエンド開発者のオープンポジションを確認してください
または、開いているアプリケーションを送ってください!
