「GoLanguageBible」研究ノート第11章テスト

「GoLanguageBible」研究ノート第11章テスト


目次

  1. テストに行く
  2. テスト機能
  3. テストカバレッジ
  4. ベンチマーク
  5. 解剖学
  6. 関数の例

注:「GoLanguage Bible」のメモを調べるには、クリックしてPDFをダウンロードし、本を読むことをお勧めします。
初心者向けの語学学習ノートに行き、本の内容をコピーしてください。大物は読んだときにスプレーしません。慣れてきたら、自分のリーディングノートにまとめます。


  1. 最初のストアドプログラムコンピュータEDSACの設計者であるモーリスウィルクスは、1949年に研究室で階段を上っていたときにひらめきを覚えました。コンピュータパイオニアの回想録で、彼は次のように回想しました。「突然、悟りの感覚があり、私の人生の残りの楽しい時間は、プログラムのバグを見つけることに費やされます。」それ以来、ほとんどの通常のコーダーがウィルクスの過度に悲観的な考えに共感することは確かですが、ソフトウェア開発の難しさについての彼の素朴な見方に誰も混乱しないかもしれません。
  2. 現在のプログラムはウィルクス時代よりもはるかに大きく複雑であり、ソフトウェアの複雑さを
    制御できるようにする多くの手法がありますこれらの手法のうち2つは、実際にはより効果的であることが証明されています。1つ目は、正式にデプロイする前にコードを確認する必要があるということです
    2つ目はテストです。これは、この章で説明するトピックです。
  3. テストとは、一般的に自動テストを意味します。つまり、テスト対象のコード(製品
    コード)が期待どおりに動作することを確認するための小さなプログラムをいくつか作成します。これらは通常、特定の機能または
    境界の処理を実行するように慎重に設計されています。ランダム入力によって検証されます。
  4. ソフトウェアテストは大きな領域です。テストのタスクは、一部のプログラマーの時間の一部と他のプログラマーの時間のすべてを占めている可能性があります。ソフトウェアテスト技術に関連する何千もの本やブログ投稿があります。すべての主流のプログラミング言語には、テスト用のソフトウェアパッケージが多数あり、テスト関連の理論も多数あり、それぞれが多数の技術的なパイオニアやフォロワーを魅了しています。これらは、まったく新しいスキルのセットを再学習するための効果的なテストを作成したいプログラマーを説得するのに十分です。
  5. Go言語テストテクノロジーは比較的低レベルです。これは、go test testコマンドと、規則に従って記述された一連のテスト関数に依存しており、testコマンドはこれらのテスト関数を実行できます。比較的軽量の純粋なテストコードを書くことは効果的であり、ベンチマークテストやサンプルドキュメントに簡単に拡張できます。
  6. 実際には、テストコードの記述とプログラム自体の記述に大きな違いはありません。私たちが書く各関数は、特定のタスクごとにもあります。境界条件を注意深く処理し、適切なデータ構造を検討し、適切な入力がどのような結果出力を生成するかを推測する必要があります。テストコードのプログラミングプロセスは、通常のGoコードの記述と似ています。新しい記号、ルール、ツールを学ぶ必要はありません。

1.テストに行く

  1. go testコマンドは、テストコードの特定の規則と構成に従うドライバープログラムです。パッケージディレクトリでは、接尾辞_test.goが付いたすべてのソースファイルはgoビルドパッケージの一部ではなく、goテストテストの一部です。
  2. * _test.goファイルには、テスト関数、ベンチマークテスト関数、サンプル関数の3種類の関数があります。テスト関数は、関数名のプレフィックスとしてTestを使用する関数です。これは、プログラムの論理的な動作が正しいかどうかをテストするために使用されます。gotestコマンドは、これらのテスト関数を呼び出し、テスト結果をPASSまたはFAILとして報告します。ベンチマークテスト関数は、関数名Benchmarkのプレフィックスが付いた関数であり、一部の関数のパフォーマンスを測定するために使用されます。gotestコマンドは、ベンチマーク関数を複数回実行して平均実行時間を計算します。サンプル関数は、関数名のプレフィックスとしてExampleを使用した関数であり、コンパイラによって正確性が保証されているサンプルドキュメントを提供します
    セクション11.2でテスト関数のすべての詳細、セクション11.4でベンチマーク関数の詳細、セクション11.6でサンプル関数の詳細について説明します。
  3. go testコマンドは、上記の命名規則を満たす* _test.goファイル内のすべての関数をトラバースし、対応するテスト関数を呼び出すための一時メインパッケージを生成し、ビルドして実行し、テスト結果を報告し、最後にクリーンアップします。テスト一時ファイルで生成された関数をアップ

2.テスト機能

  1. 各テスト関数は、テストパッケージをインポートする必要があります。テスト関数には次の署名があります。
    ここに画像の説明を挿入
  2. テスト関数の名前はTestで始まる必要があり、オプションのサフィックス名は大文字で始まる必要があります。
    ここに画像の説明を挿入
  3. tパラメーターは、テストの失敗と追加のログ情報を報告するために使用されます。サンプルパッケージgopl.io/ch11/word1を定義しましょう。このパッケージには、文字列が前から後ろ、後ろから前に読み取られるかどうかをチェックする関数IsPalindromeが1つだけあります。(次の実装では、文字列が回文であるかどうかの前後に2回テストを繰り返します。この問題については、後で説明します。)
  4. gopl.io/ch11/word1
    ここに画像の説明を挿入
  5. 同じディレクトリ内のword_test.goテストファイルには、TestPalindromeとTestNonPalindromeの2つのテスト関数が含まれています。それぞれ、IsPalindromeが正しい結果をもたらすかどうかをテストし、t.Errorを使用して障害情報を報告します。
    ここに画像の説明を挿入
  6. go testコマンドでパラメーター付きのパッケージが指定されていない場合、現在のディレクトリに対応するパッケージがデフォルトで使用されます(go buildコマンドと同じ)。次のコマンドを使用して、テストをビルドして実行できます。
    ここに画像の説明を挿入
  7. 結果は非常に満足のいくものです。このプログラムを実行しましたが、バグレポートが発生していないため、早期に終了しませんでした。ただし、「Noelle Eve Elleon」という名前のフランスのユーザーは、IsPalindrome関数が「été」を認識しないと文句を言います。米国中部のユーザーからのもう1つの苦情は、「男、計画、運河:パナマ」が認識されていないことです。特別で小さなBUGレポートを実行すると、新しくより自然なテストケースが提供されます。
    ここに画像の説明を挿入
  8. 長い文字列を2回入力しないようにするために、Printfと同様のフォーマット関数を提供するErrorf関数を使用して、エラー結果を報告します。
  9. これらの2つのテストケースを追加した後、gotestはテストの失敗情報を返します
    ここに画像の説明を挿入
  10. 最初にテストケースを作成し、テストケースがユーザーから報告されたエラーと同じ説明をトリガーすることを確認することをお勧めします。この方法でのみ、本当に解決したい問題を見つけることができます。
  11. テストケースを最初に作成するもう1つの利点は、通常、テストの実行がレポート処理を手動で記述するよりも高速であるため、迅速に反復できることです。テストセットに実行速度の遅いテストが多数ある場合は、実行する特定のテストのみを選択することで、テストを高速化できます。
  12. パラメータ-vを使用して、各テスト関数の名前と実行時間を出力できます。
    ここに画像の説明を挿入
  13. パラメータ-runは正規表現に対応し、テスト関数名が正しく一致するテスト関数のみがgo testtestコマンドによって実行されます。
    ここに画像の説明を挿入
  14. もちろん、失敗したテストケースを修正したら、コードの更新を送信する前に、パラメーターを指定せずにgo testコマンドを使用してすべてのテストケースを実行し、新しい問題を発生させずに失敗したテストを修正する必要があります。
  15. 私たちの仕事は、これらのエラーを修正することです。簡単な分析の結果、最初のバグの理由は、ルーンシーケンスの代わりにバイトを使用したため、「été」のéのような非ASCII文字を正しく処理できないことがわかりました。2番目のBUGは、スペースと文字の大文字と小文字を無視しないことが原因です。
  16. 上記の2つのBUGに対応して、関数を注意深く書き直しました。
  17. gopl.io/ch11/word2
    ここに画像の説明を挿入
  18. 同時に、以前のすべてのテストデータをテストテーブルにマージしました。
    ここに画像の説明を挿入
  19. これで、新しいテストに合格しました。
    ここに画像の説明を挿入
  20. この種のテーブル駆動型テストは、Go言語では非常に一般的です。新しいテストデータをテーブルに追加するのは簡単です。次のテストロジックは冗長ではないため、エラーメッセージを改善するためにより多くのエネルギーを費やすことができます。
  21. 失敗したテストの出力には、t.Errorfが呼び出された時点のスタック呼び出し情報は含まれていません。他のプログラミング言語やテストフレームワークでのアサーションのアサートとは異なり、t.Errorf呼び出しでもパニック例外が発生したり、テストの実行が停止したりすることはありませんでした。テーブルの最初のデータによってテストが失敗した場合でも、テーブルの背後にあるテストデータがテストを実行するため、テストで複数の失敗情報を学習する可能性があります。
  22. 初期化に失敗したり、以前のエラーによって後続のエラーが発生したりするなどの理由で、本当にテストを停止する必要がある場合は、t.Fatalまたはt.Fatalfを使用して現在のテスト機能を停止できます。これらは、テスト関数と同じゴルーチンで呼び出す必要があります。
  23. テスト失敗情報の一般的な形式は「f(x)= y、want z」です。ここで、f(x)は失敗した操作と対応する出力を説明し、yは実際の実行結果、zは期待される正しい結果です。回文文字列をチェックする前の例と同様に、実際の関数がf(x)部分に使用されます。表示xがテーブル駆動型テストの重要な部分である場合、同じアサーションが異なるテーブル項目に対して複数回実行される可能性があるためです。無駄で冗長な情報は避けてください。ブール型を返すIsPalindromeと同様の関数をテストする場合、追加情報なしでz部分を無視できます。x、y、またはzがyの長さである場合は、関連する部分の簡潔な要約を出力するだけです。テストの作成者は、プログラマーがテストの失敗の原因を診断できるように支援する必要があります。

1.ランダムテスト

  1. テーブル駆動型テストは、慎重に選択されたテストデータに基づいたテストケースの構築を容易にします。もう1つのテストのアイデアは、ランダムテストです。これは、より広範囲のランダム入力を作成することにより、探索関数の動作をテストすることです。
  2. では、ランダム入力の場合、どのようにして目的の出力結果を知ることができますか?2つの処理戦略があります。1つ目は、単純で明確なアルゴリズムを使用して別の制御関数を作成することです。これは非効率的ですが、動作はテストする関数と一致しています。次に、同じランダム入力について両方の出力結果を確認します。2つ目は、生成されたランダム入力データが特定のパターンに従うため、期待される出力パターンを知ることができるということです。
  3. 次の例では、2番目の方法を使用しています。randomPalindrome関数を使用して、パリンドローム文字列をランダムに生成します。
    ここに画像の説明を挿入
  4. ランダムテストには不確実性がありますが、それも非常に重要です。失敗したテストのログから十分な情報を得ることができます。この例では、IsPalindromeのpパラメータを入力すると実際のデータがわかりますが、関数がより複雑な入力を受け入れる場合は、すべての入力を保存する必要はありません。単に乱数シードをログに記録するだけです
    (上記の方法)。これらの乱数初期化シードを使用すると、テストコードを簡単に変更して、失敗したランダムテストを再現できます。
  5. 現在の時刻をランダムシードとして使用することにより、プロセス全体でテストコマンドが実行されるたびに、新しいランダムデータが探索されます。定期的に実行される自動テスト統合システムを使用している場合、ランダムテストは特に価値があります

2.コマンドをテストします

  1. テストパッケージの場合、go testは便利なツールですが、少しの努力で実行可能プログラムのテストにも使用できます。パッケージの名前がmainの場合、ビルド時に実行可能プログラムが生成されますが、メインパッケージはテスターコードによってパッケージとしてインポートできます。
  2. セクション2.3.2でエコープログラムのテストを書いてみましょう。まず、プログラムを2つの関数に分割します。echo関数が実際の作業を実行し、main関数を使用して、コマンドライン入力パラメーターとechoによって返されるエラーの可能性を処理します。
  3. gopl.io/ch11/echo
    ここに画像の説明を挿入
  4. テストでは、さまざまなパラメーターとフラグを使用してエコー関数を呼び出し、その出力が正しいかどうかを確認できます。パラメーターを増やして、エコー関数のグローバル変数への依存を減らします。また、os.Stdoutを直接使用する代わりに、outという名前のグローバル変数を追加しました。これにより、テストコードを必要に応じて別のオブジェクトに変更して、簡単に検査できるようになります。以下は、echo_test.goファイルのテストコードです。
    ここに画像の説明を挿入
  5. テストコードと製品コードは同じパッケージに含まれていることに注意してください。メインパッケージですが、対応するメインエントリ関数もありますが、メインパッケージはテスト中にTestEchoテスト関数によってインポートされた通常のパッケージであり、メイン関数はエクスポートされず、無視されます。
  6. テストをテーブルに配置することで、新しいテストケースを簡単に追加できます。次のテストケースを追加して、失敗の様子を見てみましょう。
    ここに画像の説明を挿入
  7. gotestの出力は次のとおりです。
    ここに画像の説明を挿入
  8. エラーメッセージは、試行された操作(Goのような構文を使用)、実際の結果、および期待される結果を示しています。このエラーメッセージを使用すると、コードを調べる前にエラーの原因を簡単に特定できます。
  9. これらの関数を呼び出すとプログラムが早期に終了するため、log.Fatalまたはos.Exitはテストコードでは呼び出されないことに注意してください。これらの関数を呼び出す特権は、メイン関数に配置する必要があります。予期しない何かが関数でパニック例外を引き起こした場合、テストドライバーはrecoverで例外をキャッチしようとし、現在のテストを失敗として扱う必要があります。不正なユーザー入力、ファイルが見つからない、不適切な構成ファイルなど、予期されるエラーである場合は、空でないエラーを返すことで処理する必要があります。幸いなことに(上記の事故は単なるエピソードです)、エコーの例は比較的単純であり、空でないエラーを返す必要はありません。

3.ホワイトボックステスト

  1. テスト分類の1つの方法は、テスターがテストオブジェクトの内部動作原理を理解する必要があるかどうかに基づいています。ブラックボックステストでは、テストパッケージによって公開されたドキュメントとAPIの動作のみが必要であり、内部実装はテストコードに対して透過的です。それどころか、ホワイトボックステストはパッケージの内部関数とデータ構造にアクセスできるため、通常のクライアントでは実行できないテストを実行できます。たとえば、ホワイトボックステストでは、各操作の後に不変のデータ型を検出できます。(ホワイトボックステストは単なる伝統的な名前であり、実際、クリアボックステストと呼ぶ方が正確です。)
  2. ブラックボックスとホワイトボックスのテスト方法は補完的です。ブラックボックステストは一般により堅牢であり、ソフトウェアの実装時にテストコードを更新する必要はほとんどありません。これらは、テスターが実際の顧客のニーズを理解するのに役立ち、API設計のいくつかの欠点を見つけるのにも役立ちます。対照的に、ホワイトボックステストは、いくつかのトリッキーな内部実装に対してより多くのテストカバレッジを提供できます。
  3. テストの2つの例を見てきました。TestIsPalindromeテストは、エクスポートされたIsPalindrome関数のみを使用するため、これはブラックボックステストです。TestEchoテストは、内部エコー関数を呼び出し、内部出力パッケージレベル変数を更新します。これらは両方ともエクスポートされないため、これはホワイトボックステストです。
  4. TestEchoテストの準備をしているときに、パッケージレベルの出力変数を出力オブジェクトとして使用するようにecho関数を変更しました。これにより、テストコードで標準出力を別の実装に置き換えることができ、次のように出力されたデータを簡単に比較できます。エコー。同様の手法を使用して、製品コードの他の部分を、テストが容易な疑似オブジェクトに置き換えることができます。疑似オブジェクトを使用する利点は、簡単に構成、予測、信頼性が高く、観察しやすいことです。同時に、本番データベースの更新やクレジットカードの消費行動など、いくつかの望ましくない副作用を回避できます。
  5. 次のコードは、ユーザーにネットワークストレージを提供するWebサービスのクォータ検出ロジックを示しています。ユーザーがストレージクォータの90%を超えて使用すると、リマインダーメールが送信されます。
  6. gopl.io/ch11/storage1
    ここに画像の説明を挿入
  7. このコードをテストしたいのですが、実際の電子メールを送信したくありません。したがって、メール処理ロジックをプライベートnotifyUser関数に配置します。
  8. gopl.io/ch11/storage2
    ここに画像の説明を挿入
  9. これで、テストで実際の電子メール送信機能を偽の電子メール送信機能に置き換えることができます。通知するユーザーと電子メールの内容を記録するだけです。
    ここに画像の説明を挿入
  10. ここに問題があります。notifyUsersは引き続きテスト関数の疑似電子メール関数を使用するため、テスト関数が戻ると、CheckQuotaは正しく機能しません(グローバルオブジェクトを更新するときに常にこのリスクがあります)。後続のテストがnotifyUsersに影響を与えないように、テストコードを変更してnotifyUsersの元の状態を復元する必要があります。テストの失敗やパニックの例外を含め、すべての実行パスを復元できることを確認する必要があります。この場合、deferステートメントを使用してリカバリコードの実行を遅らせることをお勧めします。
    ここに画像の説明を挿入
  11. この処理モードを使用すると、コマンドラインフラグパラメーター、デバッグオプション、最適化パラメーターなど、すべてのグローバル変数を一時的に保存および復元できます。フック関数をインストールおよび削除して、本番コードにデバッグ情報を生成させたり、本番コードに入力を促したりできます。タイムアウト、エラー、さらには意図的に作成された同時動作など、いくつかの重要なステータス変更。
  12. go testコマンドは複数のテストを同時に実行しないため、この方法でグローバル変数を使用しても安全です。

つづく…

おすすめ

転載: blog.csdn.net/weixin_41910694/article/details/106399460