Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 18769] ファイルの概要

このコミットは、Go言語の標準ライブラリである net/http パッケージ内のテストの不安定性(flakiness)を修正するものです。具体的には、client_test.go ファイル内の TestClientInsecureTransport テストにおいて、期待されるエラーメッセージのチェックをより堅牢なものに変更しています。

コミット

commit 3b961ba3d2e442f3613d466c3a2c898132a65a16
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Wed Mar 5 14:56:50 2014 -0800

    net/http: deflake a test
    
    I missed this one in codereview.appspot.com/70010050
    Same thing, but different test.
    
    Fixes windows-amd64-race and likely other Windows
    machines failing like:
    http://build.golang.org/log/0382bf0048bf5835a51a8a902df5c6fc73cd7ff5
    
    LGTM=adg
    R=rsc, adg
    CC=golang-codereviews
    https://golang.org/cl/71770043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/3b961ba3d2e442f3613d466c3a2c898132a65a16

元コミット内容

net/http: deflake a test

このコミットは、以前のコードレビュー codereview.appspot.com/70010050 で見落とされた、別のテストにおける同様の不安定性を修正するものです。特に windows-amd64-race 環境やその他のWindowsマシンで発生していたテストの失敗(例: http://build.golang.org/log/0382bf0048bf5835a51a8a902df5c6fc73cd7ff5)を修正します。

変更の背景

ソフトウェア開発において、テストはコードの品質と信頼性を保証するために不可欠です。しかし、テストの中には「flaky test(不安定なテスト)」と呼ばれるものがあります。これは、コードの変更がないにもかかわらず、実行するたびに成功したり失敗したりするテストのことです。不安定なテストは、CI/CDパイプラインにおいて誤った失敗を報告し、開発者の時間を無駄にし、テスト結果への信頼を損なうため、非常に厄介です。

このコミットの背景には、Goのビルドシステム(Go build system)上で net/http パッケージの特定のテストがWindows環境で不安定に失敗するという問題がありました。コミットメッセージに記載されているビルドログ http://build.golang.org/log/0382bf0048bf5835a51a8a902df5c6fc73cd7ff5 を見ると、テストがタイムアウトしたり、期待しないエラーメッセージを報告したりしていることがわかります。

具体的には、TestClientInsecureTransport というテストが、TLSハンドシェイクエラーが発生した際に、ログに出力されるエラーメッセージが bad certificate となることを期待していました。しかし、実際の環境やGoのバージョン、あるいは基盤となるネットワークスタックの挙動によっては、エラーメッセージが TLS handshake error となる場合があり、これがテストの不安定性の原因となっていました。

この修正は、テストがより広範なエラーメッセージのバリエーションに対応できるようにすることで、テストの信頼性を向上させ、開発者が実際のバグに集中できるようにすることを目的としています。

前提知識の解説

Go言語の net/http パッケージ

net/http パッケージは、Go言語でHTTPクライアントとサーバーを実装するための標準ライブラリです。このパッケージは、HTTP/1.1およびHTTP/2プロトコルをサポートし、WebアプリケーションやAPIの構築に広く利用されています。

  • HTTPクライアント: http.Client 構造体を通じて、HTTPリクエストを送信し、レスポンスを受信するための機能を提供します。リクエストのタイムアウト、リダイレクトの処理、Cookieの管理、TLS設定などが可能です。
  • TLS (Transport Layer Security): HTTPS通信のセキュリティを確保するためのプロトコルです。クライアントとサーバー間の通信を暗号化し、データの完全性と認証を提供します。TLSハンドシェイクは、クライアントとサーバーが安全な通信チャネルを確立するプロセスです。このプロセス中に証明書の検証などが行われます。

Go言語のテスト

Go言語には、標準でテストフレームワークが組み込まれています。

  • testing パッケージ: Goのテストは testing パッケージを使用して記述されます。テストファイルは通常、テスト対象のファイルと同じディレクトリに _test.go サフィックスを付けて配置されます。
  • テスト関数: func TestXxx(*testing.T) という形式の関数がテスト関数として認識されます。*testing.T はテストの状態を管理し、エラー報告などの機能を提供します。
  • t.Errorf: テストが失敗したことを報告するために使用されます。指定されたフォーマット文字列と引数でエラーメッセージを出力します。
  • strings.Contains: Goの標準ライブラリ strings パッケージの関数で、ある文字列が別の文字列に含まれているかどうかをチェックします。

Flaky Test (不安定なテスト)

Flaky testとは、同じコードベースに対して複数回実行されたときに、成功と失敗がランダムに発生するテストのことです。これは、テストが外部要因(ネットワークの遅延、データベースの状態、並行処理のタイミング、環境設定など)に依存している場合や、テスト自体が非決定的なロジックを含んでいる場合に発生します。不安定なテストは、CI/CDパイプラインの信頼性を低下させ、開発者がテスト結果を信用できなくなる原因となります。

TLS Handshake Error

TLSハンドシェイクエラーは、クライアントとサーバーが安全なTLS接続を確立しようとする際に発生する問題です。これには様々な原因が考えられますが、一般的なものとしては以下のようなものがあります。

  • 証明書の検証失敗: サーバーの証明書が信頼できない、期限切れ、ホスト名と一致しないなどの問題がある場合。
  • プロトコルバージョンの不一致: クライアントとサーバーがサポートするTLSプロトコルバージョンが一致しない場合。
  • 暗号スイートの不一致: クライアントとサーバーが共通の暗号スイート(暗号化アルゴリズムの組み合わせ)を合意できない場合。
  • ネットワークの問題: ハンドシェイク中にネットワーク接続が切断された場合。

このコミットでは、bad certificate という具体的なエラーメッセージではなく、より一般的な TLS handshake error というメッセージを期待することで、テストの堅牢性を高めています。これは、証明書の問題だけでなく、TLSハンドシェイク中に発生する可能性のある他のエラーも包括的に捉えるためです。

技術的詳細

このコミットは、src/pkg/net/http/client_test.go 内の TestClientInsecureTransport 関数におけるエラーメッセージのチェックロジックを変更しています。

元のコードでは、テストが errc チャネルから受信したエラーメッセージ vbad certificate という文字列を含んでいることを期待していました。

if !strings.Contains(v, "bad certificate") {
    t.Errorf("expected an error log message containing 'bad certificate'; got %q", v)
}

しかし、Goのランタイムや基盤となるOSのTLS実装の挙動によっては、TLSハンドシェイクが失敗した際にログに出力されるエラーメッセージが常に bad certificate とは限らないことが判明しました。例えば、Windows環境では、より一般的な TLS handshake error というメッセージが出力されることがありました。

この不一致が、テストの不安定性の原因となっていました。テストは、TLSハンドシェイクが失敗したという本質的な事象を検証したいのであって、そのエラーメッセージの厳密な文字列を検証したいわけではありません。エラーメッセージの具体的な内容は、GoのバージョンアップやOSの変更によって変わりうるため、それに依存するテストは不安定になりがちです。

このコミットでは、期待するエラーメッセージを TLS handshake error に変更することで、この問題を解決しています。

if !strings.Contains(v, "TLS handshake error") {
    t.Errorf("expected an error log message containing 'TLS handshake error'; got %q", v)
}

TLS handshake error は、bad certificate よりも広範なエラーカテゴリをカバーしており、TLSハンドシェイク中に発生する様々な問題(証明書の問題を含む)を示すことができます。これにより、テストはTLSハンドシェイクの失敗という本質的な挙動をより確実に捉えることができ、特定の環境やエラーの詳細に依存しない、より堅牢なものとなります。

この変更は、テストの意図をより正確に反映し、環境による差異を吸収することで、テストの信頼性と安定性を向上させる典型的な「flaky test」の修正例と言えます。

コアとなるコードの変更箇所

--- a/src/pkg/net/http/client_test.go
+++ b/src/pkg/net/http/client_test.go
@@ -599,8 +599,8 @@ func TestClientInsecureTransport(t *testing.T) {
 
 	select {\n \tcase v := <-errc:\n-\t\tif !strings.Contains(v, \"bad certificate\") {\n-\t\t\tt.Errorf(\"expected an error log message containing \'bad certificate\'; got %q\", v)\n+\t\tif !strings.Contains(v, \"TLS handshake error\") {\n+\t\t\tt.Errorf(\"expected an error log message containing \'TLS handshake error\'; got %q\", v)\n \t\t}\n \tcase <-time.After(5 * time.Second):\n \t\tt.Errorf(\"timeout waiting for logged error\")\n```

## コアとなるコードの解説

変更されたのは、`src/pkg/net/http/client_test.go` ファイル内の `TestClientInsecureTransport` 関数です。このテストは、安全でないトランスポート(おそらく無効な証明書を持つサーバーなど)を使用した場合のクライアントの挙動をテストしています。

具体的には、`select` ステートメント内の `case v := <-errc:` ブロックが変更されています。
-   **変更前**: `if !strings.Contains(v, "bad certificate") { ... }`
    -   これは、`errc` チャネルから受信したエラーメッセージ `v` が `"bad certificate"` という文字列を含んでいない場合にテストを失敗させる条件でした。
-   **変更後**: `if !strings.Contains(v, "TLS handshake error") { ... }`
    -   これは、エラーメッセージ `v` が `"TLS handshake error"` という文字列を含んでいない場合にテストを失敗させるように変更されました。

この変更により、テストはより一般的なTLSハンドシェイクエラーを検知するようになり、特定の環境で発生する可能性のあるエラーメッセージのバリエーションに対応できるようになりました。これにより、テストの不安定性が解消され、より信頼性の高いテスト結果が得られるようになります。

## 関連リンク

-   GitHubコミットページ: [https://github.com/golang/go/commit/3b961ba3d2e442f3613d466c3a2c898132a65a16](https://github.com/golang/go/commit/3b961ba3d2e442f3613d466c3a2c898132a65a16)
-   Go Code Review (CL): [https://golang.org/cl/71770043](https://golang.org/cl/71770043)
-   関連するビルドログ: [http://build.golang.org/log/0382bf0048bf5835a51a8a902df5c6fc73cd7ff5](http://build.golang.org/log/0382bf0048bf5835a51a8a902df5c6fc73cd7ff5)

## 参考にした情報源リンク

-   Go言語公式ドキュメント `net/http` パッケージ: [https://pkg.go.dev/net/http](https://pkg.go.dev/net/http)
-   Go言語公式ドキュメント `testing` パッケージ: [https://pkg.go.dev/testing](https://pkg.go.dev/testing)
-   Go言語公式ドキュメント `strings` パッケージ: [https://pkg.go.dev/strings](https://pkg.go.dev/strings)
-   TLSハンドシェイクに関する一般的な情報 (例: MDN Web Docs): [https://developer.mozilla.org/ja/docs/Web/Security/Transport_Layer_Security](https://developer.mozilla.org/ja/docs/Web/Security/Transport_Layer_Security)
-   Flaky testsに関する一般的な情報 (例: Martin Fowler's blog): [https://martinfowler.com/articles/flakyTests.html](https://martinfowler.com/articles/flakyTests.html)