Go: Go Testing:`testing` パッケージ、テーブル駆動型テスト、ベンチマーク、および

Goの標準ライブラリには、組み込みのテストフレームワークが用意されています。サードパーティ製のテストライブラリやアサートライブラリは必要なく、基本的なtestingパッケージだけで高品質なテストを作成できます。

Goに組み込まれているテストツールチェーンは、業界でも類を見ないものです。go testがテスト関数を自動的に検出し、テーブル駆動型テストはGoコミュニティの代名詞であり、ベンチマークやコードカバレッジも最初から利用可能です。このレッスンでは、Goのテストに関するすべての核心的な要素を習得します。

1. 学習内容



2. リファクタリングエンジニアの実話

(1) 課題:1つの機能を変更しただけで、3つのモジュールが同時にクラッシュしてしまった

アリスは決済チームのバックエンドエンジニアです。彼女は、決済モジュール内の税額計算ロジックのリファクタリングを依頼されました:

「『大丈夫だろう』と思って、あるフィールドの名前を変更しただけだった。ところがリリース後、注文の30%で税額の計算に誤りが見つかり、プロジェクトマネージャーによると5,000ドルの損失が出たそうだ。テストを行っていなかったため、誰がどこを変更したのか、誰も分からなかった。」

彼女は決済モジュールのソースコードを開いてみると、プロジェクト全体にテストファイルが1つもなかったことが判明した:

GO
// payment.go — no corresponding payment_test.go
func CalculateTax(amount float64, country string) float64 {
    // No one knows if this function is correct—there are no tests
    switch country {
    case "US":
        return amount * 0.08
    case "CN":
        return amount * 0.13
    default:
        return amount * 0.10
    }
}

テストを実行した後、アリスはすぐに問題に気づきました。彼女は以前、「UK」の税率を、正しいVAT率である20%ではなく20%に設定していたのです(実際には20%でしたが、amount=0というエッジケースではNaNが返されていました)。

(2) Go Solution:組み込みのテストフレームワーク

GO
// payment_test.go
package main

import "testing"

// Table-driven test
func TestCalculateTax(t *testing.T) {
    tests := []struct {
        name    string
        amount  float64
        country string
        want    float64
    }{
        {"US standard", 100.0, "US", 8.0},
        {"CN standard", 100.0, "CN", 13.0},
        {"UK default", 100.0, "UK", 10.0},
        {"zero amount", 0.0, "US", 0.0},
        {"negative amount", -50.0, "US", -4.0},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got := CalculateTax(tt.amount, tt.country)
            if got != tt.want {
                t.Errorf("CalculateTax(%v, %s) = %v, want %v",
                    tt.amount, tt.country, got, tt.want)
            }
        })
    }
}

テストを実行:

BASH
$ go test -v
=== RUN   TestCalculateTax/US_standard
=== RUN   TestCalculateTax/CN_standard
=== RUN   TestCalculateTax/UK_default
=== RUN   TestCalculateTax/zero_amount
=== RUN   TestCalculateTax/negative_amount
--- PASS: TestCalculateTax (0.00s)
    --- PASS: TestCalculateTax/US_standard (0.00s)
    --- PASS: TestCalculateTax/CN_standard (0.00s)
    --- PASS: TestCalculateTax/UK_default (0.00s)
    --- PASS: TestCalculateTax/zero_amount (0.00s)
    --- PASS: TestCalculateTax/negative_amount (0.00s)
PASS
ok      payment 0.123s

(3) 結果:テスト実施時と未実施時

次元 未試験 試験
自信を取り戻す 一行でも変更するのが怖い 変更後すぐに go test を実行する
位置決めに関する問題 起動後のユーザーエラー報告 開発段階での不具合
コードの品質 直感 データ駆動型
はじめに 変更を加えるのが怖い 変更を加えてテストを実行すれば、安心できます
返品コスト 手動による確認 自動化
💡 ヒント: Goコミュニティにおけるベストプラクティスは、テーブル駆動型テストです。これは、テストケースをテーブル(構造体のスライス)として整理し、1つのケースにつき1つのサブテストを定義する手法です。このパターンは、関数単位のテストよりも明確で、拡張性にも優れています。



3. testing.T: ユニットテストの基礎

(1) テスト関数の規則

GO
// Rules:
// 1. File name must end with _test.go
// 2. Function signature must be func TestXxx(t *testing.T)
// 3. Xxx must start with an uppercase letter

// math_test.go
package main

import "testing"

func TestAdd(t *testing.T) {
    got := Add(2, 3)
    want := 5
    if got != want {
        t.Errorf("Add(2,3) = %d, want %d", got, want)
    }
}

func TestSubtract(t *testing.T) {
    got := Subtract(5, 3)
    want := 2
    if got != want {
        t.Errorf("Subtract(5,3) = %d, want %d", got, want)
    }
}

(2) テストにおける一般的な手法。T

メソッド アクション 続行しますか?
t.Log(args...) ログを出力する(-v が指定された場合にのみ表示される)
t.Error(args...) 失敗としてマーク + 実行を続行
t.Errorf(format, args...) 書式エラー
t.Fatal(args...) 失敗としてマーク + 現在のテストを停止
t.Fatalf(format, args...) フォーマットエラー
t.Skip(args...) このテストをスキップ

▶ サンプル:テスト関数の4つの書き方

GO 📖 参照専用
package main

import (
    "fmt"
    "testing"
)

// Function under test
func Divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, ErrDivisionByZero
    }
    return a / b, nil
}

var ErrDivisionByZero = fmt.Errorf("division by zero")

// Style 1: Simple assertion
func TestDivideBasic(t *testing.T) {
    result, err := Divide(10, 2)
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
    if result != 5.0 {
        t.Errorf("got %f, want %f", result, 5.0)
    }
}

// Style 2: Error vs Fatal
func TestDivideByZero(t *testing.T) {
    _, err := Divide(10, 0)
    if err == nil {
        t.Fatal("expected error, got nil")
    }
    if err.Error() != "division by zero" {
        t.Errorf("wrong error message: %v", err)
    }
}

// Style 3: Table-driven test
func TestDivideTable(t *testing.T) {
    tests := []struct {
        name   string
        a, b   float64
        want   float64
        wantErr bool
    }{
        {"10/2", 10, 2, 5, false},
        {"0/5", 0, 5, 0, false},
        {"-6/3", -6, 3, -2, false},
        {"1/0", 1, 0, 0, true},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := Divide(tt.a, tt.b)
            if tt.wantErr {
                if err == nil {
                    t.Error("expected error")
                }
                return
            }
            if err != nil {
                t.Fatalf("unexpected error: %v", err)
            }
            if got != tt.want {
                t.Errorf("got %f, want %f", got, tt.want)
            }
        })
    }
}
論理コード 60 行(40 行制限超過、参照専用)

4. テーブル駆動型テスト(Go言語ならではのスタイル)

(1) 標準テンプレート

GO
package main

import "testing"

func TestMax(t *testing.T) {
    // 1. Define test table
    tests := []struct {
        name string     // Test name (for subtests)
        a, b int        // Input
        want int        // Expected output
    }{
        {name: "first larger", a: 10, b: 3, want: 10},
        {name: "second larger", a: 3, b: 10, want: 10},
        {name: "equal", a: 5, b: 5, want: 5},
        {name: "negative", a: -3, b: -10, want: -3},
        {name: "zero", a: 0, b: 5, want: 5},
    }

    // 2. Iterate through table
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            // 3. Execute + assert
            got := Max(tt.a, tt.b)
            if got != tt.want {
                t.Errorf("Max(%d, %d) = %d, want %d",
                    tt.a, tt.b, got, tt.want)
            }
        })
    }
}

func Max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

▶ サンプル:テーブル駆動型+複雑な入力

GO 📖 参照専用
package main

import (
    "testing"
)

// Function under test: validate password strength
type PasswordStrength int

const (
    Weak PasswordStrength = iota
    Medium
    Strong
)

func CheckPassword(pwd string) PasswordStrength {
    if len(pwd) < 6 {
        return Weak
    }
    if len(pwd) >= 12 {
        return Strong
    }
    return Medium
}

func TestCheckPassword(t *testing.T) {
    tests := []struct {
        name string
        pwd  string
        want PasswordStrength
    }{
        {"short", "abc", Weak},
        {"medium 6", "abcdef", Medium},
        {"medium 8", "abcdefgh", Medium},
        {"strong 12", "abcdefghijkl", Strong},
        {"empty", "", Weak},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got := CheckPassword(tt.pwd)
            if got != tt.want {
                t.Errorf("CheckPassword(%q) = %v, want %v",
                    tt.pwd, got, tt.want)
            }
        })
    }
}
論理コード 41 行(40 行制限超過、参照専用)

5. testing.B ベンチマークテスト

(1) ベンチマークの基礎

GO
package main

import (
    "testing"
)

// Benchmark function: func BenchmarkXxx(b *testing.B)
func BenchmarkAdd(b *testing.B) {
    a, c := 100, 200
    for i := 0; i < b.N; i++ {
        Add(a, c)
    }
}
BASH
$ go test -bench=.
goos: darwin
goarch: amd64
pkg: example
BenchmarkAdd-8    1000000000    0.25 ns/op
PASS
ok      example 0.3s

▶ サンプル:2つの文字列連結方法のパフォーマンス比較

GO
// concat_test.go
package main

import (
    "strings"
    "testing"
)

func ConcatPlus(n int) string {
    s := ""
    for i := 0; i < n; i++ {
        s += "a"
    }
    return s
}

func ConcatBuilder(n int) string {
    var sb strings.Builder
    sb.Grow(n)
    for i := 0; i < n; i++ {
        sb.WriteByte('a')
    }
    return sb.String()
}

func BenchmarkConcatPlus(b *testing.B) {
    for i := 0; i < b.N; i++ {
        ConcatPlus(1000)
    }
}

func BenchmarkConcatBuilder(b *testing.B) {
    for i := 0; i < b.N; i++ {
        ConcatBuilder(1000)
    }
}
▶ 試してみよう
BASH
$ go test -bench=. -benchmem
BenchmarkConcatPlus-8          13134     91238 ns/op   530296 allocs/op
BenchmarkConcatBuilder-8      283321      4221 ns/op       56 allocs/op
💡 ヒント: + を使用して1,000回連結を行うと、Builderを使用する場合に比べて処理速度が20倍遅くなり、メモリ使用量は10,000倍になります。— -benchmem オプションを使用すると、メモリ使用量の差を確認できます。

(3) ベンチマーク結果の解釈

出力項目 意味
BenchmarkConcatBuilder-8 テスト名-8 (8 CPU)
283321 b.N = 283321 反復
4221 ns/op 1回の演算あたり4221ナノ秒
56 allocs/op 1回の操作あたり56回のメモリ割り当て


6. TestMain:テストのエントリポイント

(1) TestMain:セットアップ/テアダウン

GO
// main_test.go
package main

import (
    "fmt"
    "os"
    "testing"
)

func TestMain(m *testing.M) {
    // Setup (runs once for the entire package)
    fmt.Println("=== Setup: Initializing database connection ===")

    // Run all tests
    code := m.Run()

    // Teardown
    fmt.Println("=== Teardown: Closing database connection ===")

    os.Exit(code)
}

func TestSomething(t *testing.T) {
    t.Log("Test A")
}

func TestAnother(t *testing.T) {
    t.Log("Test B")
}
BASH
$ go test -v
=== Setup: Initializing database connection ===
=== RUN   TestSomething
    main_test.go:16: Test A
--- PASS: TestSomething (0.00s)
=== RUN   TestAnother
    main_test.go:20: Test B
--- PASS: TestAnother (0.00s)
=== Teardown: Closing database connection ===
PASS
ok      example 0.1s

▶ サンプル:testing.Helper ヘルパー関数

GO
package main

import "testing"

func Add(a, b int) int { return a + b }

func assertEqual(t testing.TB, got, want interface{}) {
    t.Helper()
    if got != want {
        t.Errorf("got %v, want %v", got, want)
    }
}

func TestWithHelper(t *testing.T) {
    assertEqual(t, Add(2, 3), 5)
    assertEqual(t, Add(0, 0), 0)
    assertEqual(t, Add(-1, 1), 0)
}
▶ 試してみよう
💡 ヒント: t.Helper() を使用すると、エラーメッセージがヘルパー関数内部ではなく、呼び出し元の行番号まで遡って追跡できるようになります。これは、テスト用ユーティリティ関数を記述する際の重要なベストプラクティスです。



7. 適用範囲

(1) 適用根拠

GO
// math.go
package main

func IsEven(n int) bool {
    return n%2 == 0
}

func IsPositive(n int) bool {
    return n > 0
}
GO
// math_test.go
package main

import "testing"

func TestIsEven(t *testing.T) {
    tests := []struct {
        n    int
        want bool
    }{
        {2, true},
        {3, false},
    }
    for _, tt := range tests {
        if got := IsEven(tt.n); got != tt.want {
            t.Errorf("IsEven(%d) = %v", tt.n, got)
        }
    }
}
BASH
$ go test -coverprofile=coverage.out
ok      example 0.1s    coverage: 50.0% of statements

$ go tool cover -html=coverage.out  # Open coverage report in browser

(2) go test -cover: 一般的なコマンド

コマンド 機能
go test -cover 端末に表示される通信範囲
go test -coverprofile=c.out 出力カバレッジプロファイルファイル
go tool cover -html=c.out ブラウザでビジュアルレポートを表示
go test -covermode=count 各行が実行された回数を記録する
🔥 よくある間違い: コードカバレッジ100% ≠ バグのないコード。コードカバレッジは、どの行が実行されたかを示すだけであり、ロジックが正しいかどうかは示しません。テストを書く目的は、特定のコードカバレッジ数値を達成することではなく、クリティカルパスに対する確信を得ることです。



8. httptest による HTTP テスト

(1) httptest.Server + httptest.ResponseRecorder

GO
package main

import (
    "encoding/json"
    "net/http"
    "net/http/httptest"
    "testing"
)

// Handler under test
type UserHandler struct{}

func (h UserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    resp := map[string]string{"status": "ok", "message": "hello"}
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(resp)
}

// Test 1: httptest.ResponseRecorder (testing the handler itself)
func TestUserHandler(t *testing.T) {
    req := httptest.NewRequest("GET", "/", nil)
    rec := httptest.NewRecorder()

    handler := UserHandler{}
    handler.ServeHTTP(rec, req)

    if rec.Code != http.StatusOK {
        t.Errorf("got status %d, want %d", rec.Code, http.StatusOK)
    }

    var resp map[string]string
    json.Unmarshal(rec.Body.Bytes(), &resp)
    if resp["status"] != "ok" {
        t.Errorf("got status %q, want %q", resp["status"], "ok")
    }
}

// Test 2: httptest.Server (testing a complete HTTP service)
func TestUserHandlerWithServer(t *testing.T) {
    server := httptest.NewServer(UserHandler{})
    defer server.Close()

    resp, err := http.Get(server.URL)
    if err != nil {
        t.Fatal(err)
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        t.Errorf("got %d, want %d", resp.StatusCode, http.StatusOK)
    }
}

▶ サンプル:httptest + テーブル駆動型

GO 📖 参照専用
package main

import (
    "encoding/json"
    "net/http"
    "net/http/httptest"
    "testing"
)

// Handler under test
func greetingHandler(w http.ResponseWriter, r *http.Request) {
    name := r.URL.Query().Get("name")
    if name == "" {
        name = "World"
    }
    resp := map[string]string{"message": "Hello, " + name + "!"}
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(resp)
}

func TestGreetingHandler(t *testing.T) {
    tests := []struct {
        name   string
        query  string
        want   string
        status int
    }{
        {"with name", "name=Alice", "Hello, Alice!", 200},
        {"empty name", "", "Hello, World!", 200},
        {"with special chars", "name=Go+Lang", "Hello, Go Lang!", 200},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            req := httptest.NewRequest("GET", "/?"+tt.query, nil)
            rec := httptest.NewRecorder()

            greetingHandler(rec, req)

            if rec.Code != tt.status {
                t.Errorf("status = %d, want %d", rec.Code, tt.status)
            }

            var resp map[string]string
            json.Unmarshal(rec.Body.Bytes(), &resp)
            if resp["message"] != tt.want {
                t.Errorf("message = %q, want %q", resp["message"], tt.want)
            }
        })
    }
}
論理コード 43 行(40 行制限超過、参照専用)

(3) httptest の 2 つのモード

モード httptest.NewRecorder httptest.NewServer
テスト対象 シングルハンドラー 完全なHTTPサービス
起動時のオーバーヘッド なし あり(ランダムなポートでリスニング)
ユースケース 単体テスト 統合テスト
ミドルウェアはテスト可能か? ✅ 手動で構築 ✅ パイプライン全体を通じて自動的に


9. 完全な例:決済モジュールのテストスイートのリファクタリング

GO
// payment_test.go
package main

import (
    "encoding/json"
    "errors"
    "net/http"
    "net/http/httptest"
    "strings"
    "testing"
)

// ---------- Code under test ----------

type PaymentRequest struct {
    UserID  string  `json:"user_id"`
    Amount  float64 `json:"amount"`
    Country string  `json:"country"`
}

type PaymentResponse struct {
    Success bool   `json:"success"`
    Message string `json:"message,omitempty"`
    Tax     float64 `json:"tax,omitempty"`
    Total   float64 `json:"total,omitempty"`
}

func CalculateTax(amount float64, country string) (float64, error) {
    if amount < 0 {
        return 0, errors.New("negative amount")
    }
    switch country {
    case "US":
        return amount * 0.08, nil
    case "CN":
        return amount * 0.13, nil
    case "DE":
        return amount * 0.19, nil
    default:
        return amount * 0.10, nil
    }
}

func paymentHandler(w http.ResponseWriter, r *http.Request) {
    var req PaymentRequest
    json.NewDecoder(r.Body).Decode(&req)

    tax, err := CalculateTax(req.Amount, req.Country)
    if err != nil {
        json.NewEncoder(w).Encode(PaymentResponse{
            Success: false,
            Message: err.Error(),
        })
        return
    }

    json.NewEncoder(w).Encode(PaymentResponse{
        Success: true,
        Tax:     tax,
        Total:   req.Amount + tax,
    })
}

// ---------- Test code ----------

func jsonBody(s string) *strings.Reader {
    return strings.NewReader(s)
}

// 1. Unit test: CalculateTax table-driven
func TestCalculateTax(t *testing.T) {
    tests := []struct {
        name    string
        amount  float64
        country string
        want    float64
        wantErr bool
    }{
        {"US $100", 100, "US", 8.0, false},
        {"CN $100", 100, "CN", 13.0, false},
        {"DE $100", 100, "DE", 19.0, false},
        {"UK default", 100, "UK", 10.0, false},
        {"zero amount", 0, "US", 0, false},
        {"negative amount", -100, "US", 0, true},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := CalculateTax(tt.amount, tt.country)
            if tt.wantErr {
                if err == nil {
                    t.Error("expected error")
                }
                return
            }
            if err != nil {
                t.Fatalf("unexpected error: %v", err)
            }
            if got != tt.want {
                t.Errorf("CalculateTax(%v, %s) = %v, want %v",
                    tt.amount, tt.country, got, tt.want)
            }
        })
    }
}

// 2. Benchmark: different ways to calculate tax
func BenchmarkCalculateTax(b *testing.B) {
    for i := 0; i < b.N; i++ {
        CalculateTax(100.0, "US")
    }
}

// 3. HTTP test: paymentHandler
func TestPaymentHandler(t *testing.T) {
    tests := []struct {
        name        string
        body        string
        wantStatus  int
        wantSuccess bool
    }{
        {"US payment", `{"user_id":"u1","amount":100,"country":"US"}`, 200, true},
        {"CN payment", `{"user_id":"u2","amount":200,"country":"CN"}`, 200, true},
        {"negative amount", `{"user_id":"u3","amount":-50,"country":"US"}`, 200, false},
        {"invalid JSON", `not json`, 200, false},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            req := httptest.NewRequest("POST", "/pay",
                jsonBody(tt.body))
            req.Header.Set("Content-Type", "application/json")
            rec := httptest.NewRecorder()

            paymentHandler(rec, req)

            if rec.Code != tt.wantStatus {
                t.Errorf("status = %d, want %d", rec.Code, tt.wantStatus)
            }

            var resp PaymentResponse
            json.Unmarshal(rec.Body.Bytes(), &resp)
            if resp.Success != tt.wantSuccess {
                t.Errorf("success = %v, want %v", resp.Success, tt.wantSuccess)
            }
        })
    }
}
100%
sequenceDiagram
    participant Dev
    participant Terminal as go test
    participant Package as Package Under Test
    participant Coverage as coverage.out

    Dev->>Terminal: go test -v -cover
    Terminal->>Package: 1. Find *_test.go
    Terminal->>Package: 2. Call TestMain(m)
    Package->>Package: 3. Setup
    Package->>Package: 4. Execute all TestXxx
    Package->>Package: 5. Execute BenchmarkXxx
    Package->>Package: 6. Teardown
    Package-->>Terminal: PASS / FAIL
    Terminal-->>Coverage: Coverage data
    Terminal-->>Dev: Result summary
🔥 よくある間違い: テストファイルの拡張子は _test.go でなければなりません。そうでないと、go test は実行されません。関数のシグネチャは厳密に func TestXxx(t *testing.T) でなければなりません。つまり、パラメータは *testing.T であり、*testing.TTtesting.T ではありません。


❓ よくある質問

Q テーブル駆動型テストはどのように記述すればよいですか?
A []struct をテストテーブルとして定義します。各ケースには name +入力 +期待される出力が含まれます。テーブルを順に処理し、t.Run(tt.name, ...) を使用してサブテストを実行します。Goコミュニティでは、これがテスト記述の標準的な方法と見なされています。
Q ベンチマークを実行するにはどうすればよいですか?
A go test -bench=. はすべてのベンチマークを実行します。go test -bench=FuncName は特定の関数を実行します。-benchmem はメモリ割り当て情報を表示します。b.N はフレームワークによって自動的に決定されます。
Q カバレッジを確認するにはどうすればよいですか?
A go test -cover はパーセンテージを表示します。go test -coverprofile=c.out はファイルを出力します。go tool cover -html=c.out はブラウザベースの可視化機能を提供します。基準は 70% 以上で、コアロジックについては 90% 以上が推奨されます。
Q httptest を使って HTTP ハンドラをテストするにはどうすればよいですか?
A 2つの方法があります。httptest.NewRecorder() でハンドラを直接テストする方法(単体テスト)と、httptest.NewServer(handler) でテスト用の実際の HTTP サーバーを起動する方法(統合テスト)です。まずは「Recorder」の使用をお勧めします。
Q TestMain におけるセットアップとテアダウンの実行順序は?
A TestMain は、パッケージ内のすべてのテスト関数の実行前に 1 回実行されます。セットアップは m.Run() の前に実行され、テアダウンはその後に実行されます。注:出力はキャッシュされるため、終了するには os.Exit(code) を使用する必要があります。
Q go test は何をキャッシュしますか?
A デフォルトでは、go test は(コードと環境に基づく)テスト結果をキャッシュします。再度実行しても変更がない場合は、(cached) が表示されます。go test -count=1 を使用すると、強制的に再実行できます。
Q 特定のテストをスキップするにはどうすればよいですか?
A t.Skip("reason") で現在のテストをスキップできます。testing.Short()go test -short と組み合わせて、時間がかかりすぎるテストをスキップするために使用します。t.Skipf(format, args...) は、フォーマット指定付きのスキップに使用します。

📖 まとめ


📝 練習問題

  1. 基本問題(難易度 ⭐):第4課(関数)の max(nums ...int) int に対して、正の数、負の数、正と負の数の混合、単一の要素、および引数が空の場合という、以下の5つのケースを網羅するテーブル駆動型テストを作成してください。

  2. 上級演習(難易度 ⭐⭐):第10課(ファイルI/OとJSON)のExporterに対するテストを作成してください。os.CreateTempを使用してエクスポートをテストするための一時ファイルを作成し、json.Unmarshalを使用して内容が正しいことを確認し、t.Cleanupを使用して一時ファイルを削除してください。

  3. 課題(難易度:⭐⭐⭐):第7課のマルチ決済ゲートウェイシステム向けに、完全なテストスイートを作成してください:(1) MockPaymentGateway に対するテーブル駆動テスト(固定の結果を返すもの); (2) StripeとPayPalの実装の処理速度をベンチマークする;(3) httptest を使用して、決済ハンドラのJSONレスポンス形式をテストする;(4) コードカバレッジは90%以上とする。

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%