Go: Go Testing

A biblioteca padrão do Go vem com uma estrutura de testes integrada — não são necessárias bibliotecas de testes ou de asserções de terceiros; é possível escrever testes de alta qualidade usando apenas o pacote básico testing.

A cadeia de ferramentas de teste integrada do Go é única no setor: o go test detecta automaticamente as funções de teste, os testes orientados por tabelas são uma marca registrada da comunidade Go, e os benchmarks e a cobertura de código estão disponíveis logo de cara. Nesta lição, você vai dominar todos os aspectos essenciais dos testes em Go.

1. Você aprenderá


2. A história real de um engenheiro de refatoração

(1) Problema: A alteração de uma função fez com que três módulos travassem simultaneamente

Alice é engenheira de back-end na equipe de pagamentos. Ela foi encarregada de refatorar a lógica de cálculo de impostos no módulo de pagamentos:

“Acabei de alterar o nome de um campo, achando que não haveria problema. Mas, após o lançamento, 30% dos pedidos apresentavam cálculos de impostos incorretos — o gerente de projeto disse que perdemos US$ 5.000. Como não testamos a alteração, ninguém sabia onde ela havia sido feita.”

Ela abriu o código do módulo de pagamentos e descobriu que o projeto inteiro tinha zero arquivos de teste:

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
    }
}

Depois de executar o teste, Alice percebeu imediatamente o problema — ela havia definido anteriormente a alíquota para “Reino Unido” como 20%, em vez da alíquota correta de IVA de 20% (na verdade, era 20%, mas, no caso extremo em que amount=0, o resultado era NaN).

(2) Go Solution: Estrutura de testes integrada

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)
            }
        })
    }
}

Executar teste:

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) Resultados: com testes versus sem testes

Dimensão Sem teste Teste
Recuperando a confiança Medo de alterar até mesmo uma única linha Executar go test imediatamente após fazer as alterações
Problemas de posicionamento Relatos de erros dos usuários após a inicialização Falhas na fase de desenvolvimento
Qualidade do código Intuição Orientado por dados
Primeiros passos Medo de fazer alterações Depois de fazer as alterações e executar os testes, você pode ficar tranquilo
Custos de devolução Verificação manual Automação
💡 Dica: A melhor prática na comunidade Go é o teste orientado por tabelas — organizar os casos de teste em tabelas (fatias de estruturas), com um subteste por caso. Esse padrão é mais claro e mais escalável do que o teste no nível de função.


3. testing.T: Noções básicas sobre testes de unidade

(1) Regras das funções de teste

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) Métodos comuns em testes.T

Método Ação Continuar?
t.Log(args...) Imprime um log (exibido apenas quando -v é especificado)
t.Error(args...) Marcar como falha + continuar a execução
t.Errorf(format, args...) Erro de formatação
t.Fatal(args...) Marcar como reprovado + Interromper o teste atual
t.Fatalf(format, args...) Erro fatal de formatação
t.Skip(args...) Pular este teste

(3) ▶ Exemplo: Quatro maneiras de escrever uma função de teste

GO 📖 Somente leitura
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 linhas de lógica (limite de 40, somente leitura)

4. Testes orientados por tabelas (o estilo característico do Go)

(1) Modelo padrão

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
}

(2) ▶ Exemplo: Orientado por tabela + entrada complexa

GO 📖 Somente leitura
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 linhas de lógica (limite de 40, somente leitura)

5. Teste de benchmark testing.B

(1) Noções básicas sobre benchmark

GO
package main

import (
    "testing"
)

// Benchmark Função: 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) ▶ Exemplo: Comparando o desempenho de dois métodos de concatenação de 文字列

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)
    }
}
▶ Experimente
BASH
$ go test -bench=. -benchmem
BenchmarkConcatPlus-8          13134     91238 ns/op   530296 allocs/op
BenchmarkConcatBuilder-8      283321      4221 ns/op       56 allocs/op
💡 Dica: Concatenar com + 1.000 vezes é 20 vezes mais lento do que usar o Builder e aloca 10.000 vezes mais memória — a opção -benchmem permite que você veja a diferença na alocação de memória.

(3) Interpretação dos resultados dos testes de desempenho

Item de saída Significado
BenchmarkConcatBuilder-8 Nome do teste-8 (8 CPUs)
283321 b.N = 283.321 iterações
4221 ns/op 4221 nanossegundos por operação
56 allocs/op 56 alocações de memória por operação

6. TestMain: Ponto de entrada do teste

(1) TestMain: preparação / desmontagem

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

(2) ▶ Exemplo: função auxiliar 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)
}
▶ Experimente
💡 Dica: t.Helper() garante que as mensagens de erro sejam rastreadas até o número da linha do chamador, em vez de dentro da própria função auxiliar. Essa é uma prática recomendada fundamental ao escrever funções utilitárias de teste.


7. Cobertura

(1) Fundamentos da cobertura

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: Comandos comuns

Comando Função
go test -cover Cobertura exibida no terminal
go test -coverprofile=c.out Arquivo de perfil de cobertura de saída
go tool cover -html=c.out Visualizar o relatório visual em um navegador
go test -covermode=count Registra o número de vezes que cada linha é executada
🔥 Erro comum: Cobertura de código de 100% ≠ código sem bugs. A cobertura de código apenas indica quais linhas foram executadas; ela não indica se a lógica está correta. O objetivo de escrever testes não é atingir um número específico de cobertura de código, mas sim ganhar confiança no caminho crítico.


8. Teste de HTTP com o httptest

(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)
    }
}

(2) ▶ Exemplo: httptest + orientado por tabela

GO 📖 Somente leitura
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 linhas de lógica (limite de 40, somente leitura)

(3) Dois modos do httptest

Modo httptest.NewRecorder httptest.NewServer
Assunto do teste Manipulador único Serviço HTTP completo
Carga inicial Nenhuma Sim (escuta em uma porta aleatória)
Casos de uso Testes unitários Testes de integração
É possível testar o middleware? ✅ Criado manualmente ✅ Automaticamente ao longo de todo o pipeline

9. Exemplo completo: refatoração do conjunto de testes do módulo de pagamentos

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
🔥 Erro comum: Os arquivos de teste devem ter a extensão _test.go; caso contrário, go test não será executado. A assinatura da função deve ser estritamente func TestXxx(t *testing.T) — o parâmetro é *testing.T, e não *testing.TT ou testing.T.


❓ Perguntas Frequentes

P: Quais são as principais funções do pacote testing? R: testing.T (testes unitários), testing.B (testes de benchmark), testing.M (ponto de entrada do teste), testing.Helper() (anotação de função auxiliar), testing.Short() (ignora testes longos).

P: Como escrevo testes baseados em tabelas? R: Defina um []struct como a tabela de testes, em que cada caso contenha name + entrada + saída esperada. Percorra a tabela e use t.Run(tt.name, ...) para executar subtestes. A comunidade Go considera essa a maneira padrão de escrever testes.

P: Como faço para executar um teste de desempenho? R: go test -bench=. executa todos os testes de desempenho; go test -bench=FuncName executa uma função específica; -benchmem exibe informações sobre a alocação de memória. b.N é determinado automaticamente pela estrutura.

P: Como posso verificar a cobertura? R: go test -cover exibe a porcentagem; go test -coverprofile=c.out gera um arquivo; go tool cover -html=c.out oferece uma visualização no navegador. O padrão é 70% ou mais, e recomenda-se 90% ou mais para a lógica principal.

P: Como faço para usar o httptest para testar um manipulador HTTP? R: Existem duas maneiras: httptest.NewRecorder() para testar o manipulador diretamente (teste de unidade) e httptest.NewServer(handler) para iniciar um servidor HTTP real para testes (teste de integração). Recomendamos usar primeiro o Recorder.

P: Qual é a ordem de execução das funções Setup e Teardown no TestMain? R: O TestMain é executado uma vez antes de todas as funções de teste do pacote. A função Setup é executada antes de m.Run(), e a função Teardown é executada depois. Observação: a saída é armazenada em cache; portanto, é necessário usar os.Exit(code) para sair.

P: O que o go test armazena em cache? R: Por padrão, o go test armazena em cache os resultados dos testes (com base no código e no ambiente). Se não houver alterações quando você executá-lo novamente, ele exibirá (cached). Use go test -count=1 para forçar uma nova execução.

P: Como faço para pular determinados testes? R: t.Skip("reason") pula o teste atual; testing.Short() funciona em conjunto com go test -short para pular testes que demoram muito; t.Skipf(format, args...) serve para pulos formatados.


📖 Resumo


📝 Exercícios

  1. Problema básico (Dificuldade ⭐): Escreva um teste baseado em tabela para max(nums ...int) int da Lição 4 (Funções), abrangendo os cinco casos a seguir: números positivos, números negativos, uma mistura de números positivos e negativos, um único elemento e argumentos vazios.

  2. Exercício avançado (Dificuldade ⭐⭐): Escreva testes para o Exporter da Lição 10 (E/S de arquivos e JSON): use o os.CreateTemp para criar um arquivo temporário para testar a exportação; use o json.Unmarshal para verificar se o conteúdo está correto; use o t.Cleanup para limpar o arquivo temporário.

  3. Desafio (Dificuldade: ⭐⭐⭐): Escreva um conjunto completo de testes para o sistema de gateway de pagamentos múltiplos da Lição 7: (1) Testes baseados em tabelas para MockPaymentGateway (retorna resultados fixos); (2) Compare a velocidade das implementações do Stripe e do PayPal; (3) Use httptest para testar o formato de resposta JSON do manipulador de pagamentos; (4) Cobertura de código ≥ 90%.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%