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

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

コミット

all: use t.Skip{,f}

Replace various t.Log{,f} ; return checks with t.Skip{,f}.

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

https://github.com/golang/go/commit/6a9e956f194a4b22beb052fb2152f6f395d45158

元コミット内容

commit 6a9e956f194a4b22beb052fb2152f6f395d45158
Author: Dave Cheney <dave@cheney.net>
Date:   Thu Jan 24 17:32:10 2013 +1100

    all: use t.Skip{,f}
    
    Replace various t.Log{,f} ; return checks with t.Skip{,f}.
    
    R=golang-dev, n13m3y3r, bradfitz, adg, mikioh.mikioh
    CC=golang-dev
    https://golang.org/cl/7193044

変更の背景

このコミットは、Go言語の標準ライブラリ内のテストコードにおいて、テストをスキップする際の慣用的なパターンを改善することを目的としています。以前は、特定の条件(例: testing.Short() フラグが設定されている場合、特定のOSでの実行、外部ネットワークへのアクセスが必要な場合など)でテストを実行しないようにするために、t.Log または t.Logf でスキップ理由を出力し、その後に return で関数を終了させるという手法が広く用いられていました。

しかし、このパターンにはいくつかの問題がありました。

  1. 意図の不明瞭さ: t.Logreturn の組み合わせは、テストがスキップされたのか、それとも単にログを出力して早期終了しただけなのか、コードを読んだだけでは直感的に理解しにくい場合がありました。
  2. テストランナーとの連携: Goのテストフレームワークは、t.Skipt.Skipf を呼び出すことで、テストがスキップされたことを明示的に認識し、適切なレポートを生成します。t.Logreturn の組み合わせでは、テストランナーはテストが正常に完了したか、あるいは失敗したと誤解する可能性がありました。これにより、テスト結果の集計やCI/CDパイプラインでの処理において、正確な情報が得られないことがありました。
  3. 冗長性: t.Logreturn の2行を記述する必要があり、コードが冗長になる傾向がありました。

このコミットは、これらの問題を解決するために、テストをスキップする際には常に t.Skip または t.Skipf を使用するように統一することを目的としています。これにより、テストコードの可読性が向上し、テスト結果の報告がより正確になり、Goのテストフレームワークの意図に沿った利用が促進されます。

前提知識の解説

Go言語の testing パッケージ

Go言語には、ユニットテストやベンチマークテストを記述するための標準パッケージ testing が用意されています。テストファイルは通常、テスト対象のGoファイルと同じディレクトリに _test.go というサフィックスを付けて配置されます。

*testing.T

テスト関数は func TestXxx(t *testing.T) の形式で定義され、*testing.T 型の引数 t を受け取ります。この t オブジェクトは、テストの実行中に様々な操作を行うためのメソッドを提供します。

t.Logt.Logf

t.Log および t.Logf は、テストの実行中に情報をログに出力するために使用されます。t.Logffmt.Printf と同様にフォーマット文字列と引数を受け取ります。これらの関数は、テストの成功・失敗に関わらず情報を出力する際に便利ですが、テストの実行フローを制御するものではありません。

t.Skipt.Skipf

t.Skip および t.Skipf は、現在のテストをスキップするために使用されます。これらの関数が呼び出されると、テストの実行は直ちに停止し、テストランナーは当該テストがスキップされたものとして扱います。t.Skipft.Logf と同様にフォーマット文字列と引数を受け取り、スキップ理由を詳細に記述できます。

t.Skipt.Skipf を使用する主なシナリオは以下の通りです。

  • 環境依存のテスト: 特定のオペレーティングシステム、アーキテクチャ、または外部サービスが利用できない場合にテストをスキップする。
  • リソース依存のテスト: データベース接続、ネットワークアクセス、特定のファイルパスなど、テストに必要なリソースが利用できない場合にスキップする。
  • 時間のかかるテスト: go test -short フラグが指定された場合に、実行に時間がかかるテストをスキップする。これはCI/CD環境での高速なフィードバックループを可能にするためによく利用されます。
  • 未実装の機能のテスト: まだ実装されていない機能に対するテストを一時的にスキップする。

testing.Short()

testing パッケージには Short() 関数があり、これは go test -short コマンドラインフラグが設定されている場合に true を返します。開発者がテストを高速に実行したい場合に、時間のかかるテストをスキップするためにこの関数がよく利用されます。

技術的詳細

このコミットの技術的な核心は、Goのテストフレームワークにおけるテストスキップのセマンティクスを統一し、より堅牢で意図が明確なテストコードを促進することにあります。

従来の t.Log{,f}; return パターンは、テストの実行を中断するという点では t.Skip{,f} と同じ効果をもたらしますが、テストランナーに対するシグナルが異なります。

  • t.Log{,f}; return: テストランナーは、テスト関数が正常に終了したと解釈します。ログメッセージは出力されますが、テストが「スキップされた」という特別なステータスとしては認識されません。これは、テスト結果の集計ツールやCI/CDシステムが、スキップされたテストを誤って成功したテストとしてカウントしたり、あるいは単に実行されなかったテストとして扱ったりする原因となる可能性があります。
  • t.Skip{,f}: この関数が呼び出されると、testing パッケージは内部的に runtime.Goexit() を呼び出し、現在のゴルーチン(テスト関数)を終了させます。同時に、テストランナーに対して、このテストが意図的にスキップされたことを明示的に通知します。これにより、テスト結果のレポートには「スキップされたテスト」として正確に表示され、テストスイート全体の健全性をより正確に把握できるようになります。

この変更は、Goのテストフレームワークの設計思想、すなわち「テストは明確な結果(成功、失敗、スキップ)を報告すべきである」という原則に合致しています。特に、大規模なプロジェクトやCI/CD環境では、テスト結果の正確な集計と可視化が非常に重要になります。t.Skip{,f} の使用を徹底することで、テストのステータスが曖昧になることを防ぎ、テストレポートの信頼性を高めることができます。

また、t.Skipf の導入により、スキップ理由をフォーマット文字列で記述できるようになり、より詳細で分かりやすいメッセージを提供することが可能になりました。これは、テストがスキップされた原因をデバッグする際に役立ちます。

このコミットは、Goの標準ライブラリ全体にわたる広範な変更であり、多くのテストファイルに影響を与えています。これは、Goコミュニティがコードの品質と一貫性を重視していることの表れでもあります。

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

このコミットでは、Goの標準ライブラリ内の多数のテストファイルで、t.Log{,f}return の組み合わせが t.Skip{,f} に置き換えられています。以下に、代表的な変更例をいくつか示します。

src/pkg/archive/zip/zip_test.go

--- a/src/pkg/archive/zip/zip_test.go
+++ b/src/pkg/archive/zip/zip_test.go
@@ -18,8 +18,7 @@ import (
 
 func TestOver65kFiles(t *testing.T) {
  if testing.Short() {
-  t.Logf("slow test; skipping")
-  return
+  t.Skip("slow test; skipping")
  }
  buf := new(bytes.Buffer)
  w := NewWriter(buf)

ここでは、testing.Short()true の場合に、t.Logf("slow test; skipping")return でスキップしていた箇所が、t.Skip("slow test; skipping") の1行に置き換えられています。

src/pkg/crypto/x509/verify_test.go

--- a/src/pkg/crypto/x509/verify_test.go
+++ b/src/pkg/crypto/x509/verify_test.go
@@ -309,8 +309,7 @@ func TestGoVerify(t *testing.T) {
 
 func TestSystemVerify(t *testing.T) {
  if runtime.GOOS != "windows" {
-  t.Logf("skipping verify test using system APIs on %q", runtime.GOOS)
-  return
+  t.Skipf("skipping verify test using system APIs on %q", runtime.GOOS)
  }
 
  testVerify(t, true)

ここでは、runtime.GOOS が "windows" でない場合に、特定のOSに依存するテストをスキップするために t.Logfreturn を使用していた箇所が、t.Skipf に変更されています。t.Skipf を使うことで、スキップ理由にOS名を埋め込むことができます。

src/pkg/log/syslog/syslog_test.go

--- a/src/pkg/log/syslog/syslog_test.go
+++ b/src/pkg/log/syslog/syslog_test.go
@@ -41,23 +41,14 @@ func startServer(done chan<- string) {
  go runSyslog(c, done)
 }
 
-func skipNetTest(t *testing.T) bool {
- if testing.Short() {
-  // Depends on syslog daemon running, and sometimes it's not.
-  t.Logf("skipping syslog test during -short")
-  return true
- }
- return false
-}
-
 func TestNew(t *testing.T) {
  if LOG_LOCAL7 != 23<<3 {
   t.Fatalf("LOG_LOCAL7 has wrong value")
  }
- if skipNetTest(t) {
-  return
+ if testing.Short() {
+  // Depends on syslog daemon running, and sometimes it's not.
+  t.Skip("skipping syslog test during -short")
  }
-
  s, err := New(LOG_INFO|LOG_USER, "")
  if err != nil {
   t.Fatalf("New() failed: %s", err)

この例では、skipNetTest というヘルパー関数が削除され、そのロジックが直接テスト関数内にインライン化されています。そして、その中で t.Logfreturn の代わりに t.Skip が使われています。これにより、テストスキップのロジックがより直接的になり、ヘルパー関数を呼び出すオーバーヘッドもなくなります。

コアとなるコードの解説

上記の変更箇所は、すべて同じパターンに従っています。

  1. 条件判定: if 文でテストをスキップすべき条件(例: testing.Short(), runtime.GOOS != "windows", os.Getuid() != 0 など)を判定します。
  2. ログ出力と早期終了の置き換え: 従来の t.Log("reason"); return または t.Logf("reason %v", arg); return の2行が、t.Skip("reason") または t.Skipf("reason %v", arg) の1行に置き換えられています。

この変更の意図は、Goのテストフレームワークが提供する t.Skip および t.Skipf メソッドを、テストをスキップする際の標準的かつ推奨される方法として一貫して使用することです。

  • 明確な意図: t.Skip を使用することで、コードを読む開発者に対して「このテストは意図的にスキップされた」という明確なシグナルを送ります。これにより、テストが失敗したのか、それとも単に実行されなかったのかといった混乱を防ぎます。
  • 正確なテストレポート: go test コマンドやCI/CDツールは、t.Skip が呼び出されたテストを「スキップされた」として正確に報告します。これにより、テストスイート全体の健全性に関するより正確な統計情報が得られます。例えば、go test -v を実行すると、スキップされたテストは --- SKIP: TestName (duration) のように表示されます。
  • 簡潔なコード: t.Logreturn の2行が t.Skip の1行に集約されることで、コードの行数が減り、より簡潔になります。

このコミットは、Goの標準ライブラリのテストコードベース全体にわたる品質向上と一貫性の確立に貢献しています。これは、Go言語の設計哲学である「シンプルさ」と「明瞭さ」をテストコードにも適用しようとする試みの一環と言えます。

関連リンク

参考にした情報源リンク

  • Go testing package documentation: https://pkg.go.dev/testing
  • Go blog post about testing: https://go.dev/blog/testing
  • Stack Overflow discussions on t.Skip vs t.Log + return (general understanding, no specific link referenced for this commit, but common knowledge in Go community)
  • Go source code for testing package (for understanding t.Skip implementation details)
# [インデックス 14979] ファイルの概要

## コミット
all: use t.Skip{,f}

Replace various t.Log{,f} ; return checks with t.Skip{,f}.

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

[https://github.com/golang/go/commit/6a9e956f194a4b22beb052fb2152f6f395d45158](https://github.com/golang/go/commit/6a9e956f194a4b22beb052fb2152f6f395d45158)

## 元コミット内容

commit 6a9e956f194a4b22beb052fb2152f6f395d45158 Author: Dave Cheney dave@cheney.net Date: Thu Jan 24 17:32:10 2013 +1100

all: use t.Skip{,f}

Replace various t.Log{,f} ; return checks with t.Skip{,f}.

R=golang-dev, n13m3y3r, bradfitz, adg, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/7193044

## 変更の背景
このコミットは、Go言語の標準ライブラリ内のテストコードにおいて、テストをスキップする際の慣用的なパターンを改善することを目的としています。以前は、特定の条件(例: `testing.Short()` フラグが設定されている場合、特定のOSでの実行、外部ネットワークへのアクセスが必要な場合など)でテストを実行しないようにするために、`t.Log` または `t.Logf` でスキップ理由を出力し、その後に `return` で関数を終了させるという手法が広く用いられていました。

しかし、このパターンにはいくつかの問題がありました。
1.  **意図の不明瞭さ**: `t.Log` と `return` の組み合わせは、テストがスキップされたのか、それとも単にログを出力して早期終了しただけなのか、コードを読んだだけでは直感的に理解しにくい場合がありました。
2.  **テストランナーとの連携**: Goのテストフレームワークは、`t.Skip` や `t.Skipf` を呼び出すことで、テストがスキップされたことを明示的に認識し、適切なレポートを生成します。`t.Log` と `return` の組み合わせでは、テストランナーはテストが正常に完了したか、あるいは失敗したと誤解する可能性がありました。これにより、テスト結果の集計やCI/CDパイプラインでの処理において、正確な情報が得られないことがありました。
3.  **冗長性**: `t.Log` と `return` の2行を記述する必要があり、コードが冗長になる傾向がありました。

このコミットは、これらの問題を解決するために、テストをスキップする際には常に `t.Skip` または `t.Skipf` を使用するように統一することを目的としています。これにより、テストコードの可読性が向上し、テスト結果の報告がより正確になり、Goのテストフレームワークの意図に沿った利用が促進されます。

## 前提知識の解説

### Go言語の `testing` パッケージ
Go言語には、ユニットテストやベンチマークテストを記述するための標準パッケージ `testing` が用意されています。テストファイルは通常、テスト対象のGoファイルと同じディレクトリに `_test.go` というサフィックスを付けて配置されます。

### `*testing.T`
テスト関数は `func TestXxx(t *testing.T)` の形式で定義され、`*testing.T` 型の引数 `t` を受け取ります。この `t` オブジェクトは、テストの実行中に様々な操作を行うためのメソッドを提供します。

### `t.Log` と `t.Logf`
`t.Log` および `t.Logf` は、テストの実行中に情報をログに出力するために使用されます。`t.Logf` は `fmt.Printf` と同様にフォーマット文字列と引数を受け取ります。これらの関数は、テストの成功・失敗に関わらず情報を出力する際に便利ですが、テストの実行フローを制御するものではありません。

### `t.Skip` と `t.Skipf`
`t.Skip` および `t.Skipf` は、現在のテストをスキップするために使用されます。これらの関数が呼び出されると、テストの実行は直ちに停止し、テストランナーは当該テストがスキップされたものとして扱います。`t.Skipf` は `t.Logf` と同様にフォーマット文字列と引数を受け取り、スキップ理由を詳細に記述できます。

`t.Skip` や `t.Skipf` を使用する主なシナリオは以下の通りです。
*   **環境依存のテスト**: 特定のオペレーティングシステム、アーキテクチャ、または外部サービスが利用できない場合にテストをスキップする。
*   **リソース依存のテスト**: データベース接続、ネットワークアクセス、特定のファイルパスなど、テストに必要なリソースが利用できない場合にスキップする。
*   **時間のかかるテスト**: `go test -short` フラグが指定された場合に、実行に時間がかかるテストをスキップする。これはCI/CD環境での高速なフィードバックループを可能にするためによく利用されます。
*   **未実装の機能のテスト**: まだ実装されていない機能に対するテストを一時的にスキップする。

### `testing.Short()`
`testing` パッケージには `Short()` 関数があり、これは `go test -short` コマンドラインフラグが設定されている場合に `true` を返します。開発者がテストを高速に実行したい場合に、時間のかかるテストをスキップするためにこの関数がよく利用されます。

## 技術的詳細
このコミットの技術的な核心は、Goのテストフレームワークにおけるテストスキップのセマンティクスを統一し、より堅牢で意図が明確なテストコードを促進することにあります。

従来の `t.Log{,f}; return` パターンは、テストの実行を中断するという点では `t.Skip{,f}` と同じ効果をもたらしますが、テストランナーに対するシグナルが異なります。
*   `t.Log{,f}; return`: テストランナーは、テスト関数が正常に終了したと解釈します。ログメッセージは出力されますが、テストが「スキップされた」という特別なステータスとしては認識されません。これは、テスト結果の集計ツールやCI/CDシステムが、スキップされたテストを誤って成功したテストとしてカウントしたり、あるいは単に実行されなかったテストとして扱ったりする原因となる可能性があります。
*   `t.Skip{,f}`: この関数が呼び出されると、`testing` パッケージは内部的に `runtime.Goexit()` を呼び出し、現在のゴルーチン(テスト関数)を終了させます。同時に、テストランナーに対して、このテストが意図的にスキップされたことを明示的に通知します。これにより、テスト結果のレポートには「スキップされたテスト」として正確に表示され、テストスイート全体の健全性をより正確に把握できるようになります。

この変更は、Goのテストフレームワークの設計思想、すなわち「テストは明確な結果(成功、失敗、スキップ)を報告すべきである」という原則に合致しています。特に、大規模なプロジェクトやCI/CD環境では、テスト結果の正確な集計と可視化が非常に重要になります。`t.Skip{,f}` の使用を徹底することで、テストのステータスが曖昧になることを防ぎ、テストレポートの信頼性を高めることができます。

また、`t.Skipf` の導入により、スキップ理由をフォーマット文字列で記述できるようになり、より詳細で分かりやすいメッセージを提供することが可能になりました。これは、テストがスキップされた原因をデバッグする際に役立ちます。

このコミットは、Goの標準ライブラリ全体にわたる広範な変更であり、多くのテストファイルに影響を与えています。これは、Goコミュニティがコードの品質と一貫性を重視していることの表れでもあります。

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

このコミットでは、Goの標準ライブラリ内の多数のテストファイルで、`t.Log{,f}` と `return` の組み合わせが `t.Skip{,f}` に置き換えられています。以下に、代表的な変更例をいくつか示します。

### `src/pkg/archive/zip/zip_test.go`
```diff
--- a/src/pkg/archive/zip/zip_test.go
+++ b/src/pkg/archive/zip/zip_test.go
@@ -18,8 +18,7 @@ import (
 
 func TestOver65kFiles(t *testing.T) {
  if testing.Short() {
-  t.Logf("slow test; skipping")
-  return
+  t.Skip("slow test; skipping")
  }
  buf := new(bytes.Buffer)
  w := NewWriter(buf)

ここでは、testing.Short()true の場合に、t.Logf("slow test; skipping")return でスキップしていた箇所が、t.Skip("slow test; skipping") の1行に置き換えられています。

src/pkg/crypto/x509/verify_test.go

--- a/src/pkg/crypto/x509/verify_test.go
+++ b/src/pkg/crypto/x509/verify_test.go
@@ -309,8 +309,7 @@ func TestGoVerify(t *testing.T) {
 
 func TestSystemVerify(t *testing.T) {
  if runtime.GOOS != "windows" {
-  t.Logf("skipping verify test using system APIs on %q", runtime.GOOS)
-  return
+  t.Skipf("skipping verify test using system APIs on %q", runtime.GOOS)
  }
 
  testVerify(t, true)

ここでは、runtime.GOOS が "windows" でない場合に、特定のOSに依存するテストをスキップするために t.Logfreturn を使用していた箇所が、t.Skipf に変更されています。t.Skipf を使うことで、スキップ理由にOS名を埋め込むことができます。

src/pkg/log/syslog/syslog_test.go

--- a/src/pkg/log/syslog/syslog_test.go
+++ b/src/pkg/log/syslog/syslog_test.go
@@ -41,23 +41,14 @@ func startServer(done chan<- string) {
  go runSyslog(c, done)
 }
 
-func skipNetTest(t *testing.T) bool {
- if testing.Short() {
-  // Depends on syslog daemon running, and sometimes it's not.
-  t.Logf("skipping syslog test during -short")
-  return true
- }
- return false
-}
-
 func TestNew(t *testing.T) {
  if LOG_LOCAL7 != 23<<3 {
   t.Fatalf("LOG_LOCAL7 has wrong value")
  }
- if skipNetTest(t) {
-  return
+ if testing.Short() {
+  // Depends on syslog daemon running, and sometimes it's not.
+  t.Skip("skipping syslog test during -short")
  }
-
  s, err := New(LOG_INFO|LOG_USER, "")
  if err != nil {
   t.Fatalf("New() failed: %s", err)

この例では、skipNetTest というヘルパー関数が削除され、そのロジックが直接テスト関数内にインライン化されています。そして、その中で t.Logfreturn の代わりに t.Skip が使われています。これにより、テストスキップのロジックがより直接的になり、ヘルパー関数を呼び出すオーバーヘッドもなくなります。

コアとなるコードの解説

上記の変更箇所は、すべて同じパターンに従っています。

  1. 条件判定: if 文でテストをスキップすべき条件(例: testing.Short(), runtime.GOOS != "windows", os.Getuid() != 0 など)を判定します。
  2. ログ出力と早期終了の置き換え: 従来の t.Log("reason"); return または t.Logf("reason %v", arg); return の2行が、t.Skip("reason") または t.Skipf("reason %v", arg) の1行に置き換えられています。

この変更の意図は、Goのテストフレームワークが提供する t.Skip および t.Skipf メソッドを、テストをスキップする際の標準的かつ推奨される方法として一貫して使用することです。

  • 明確な意図: t.Skip を使用することで、コードを読む開発者に対して「このテストは意図的にスキップされた」という明確なシグナルを送ります。これにより、テストが失敗したのか、それとも単に実行されなかったのかといった混乱を防ぎます。
  • 正確なテストレポート: go test コマンドやCI/CDツールは、t.Skip が呼び出されたテストを「スキップされた」として正確に報告します。これにより、テストスイート全体の健全性に関するより正確な統計情報が得られます。例えば、go test -v を実行すると、スキップされたテストは --- SKIP: TestName (duration) のように表示されます。
  • 簡潔なコード: t.Logreturn の2行が t.Skip の1行に集約されることで、コードの行数が減り、より簡潔になります。

このコミットは、Goの標準ライブラリのテストコードベース全体にわたる品質向上と一貫性の確立に貢献しています。これは、Go言語の設計哲学である「シンプルさ」と「明瞭さ」をテストコードにも適用しようとする試みの一環と言えます。

関連リンク

参考にした情報源リンク

  • Go testing package documentation: https://pkg.go.dev/testing
  • Go blog post about testing: https://go.dev/blog/testing
  • Stack Overflow discussions on t.Skip vs t.Log + return (general understanding, no specific link referenced for this commit, but common knowledge in Go community)
  • Go source code for testing package (for understanding t.Skip implementation details)