测试覆盖率衡量测试代码对业务代码的覆盖程度,是评估测试质量的重要指标。
使用 Go 的覆盖率工具:
go test -cover ./...
输出类似:
ok myapp/handlers 0.123s coverage: 75.0% of statements
ok myapp/services 0.456s coverage: 60.0% of statements
生成覆盖率文件:
go test -coverprofile=coverage.out ./...
查看函数级别覆盖率:
go tool cover -func=coverage.out
输出:
main.go:15: main 100.0%
handlers/user.go:10: GetUser 80.0%
handlers/user.go:25: CreateUser 70.0%
total: 75.0%
生成可视化的 HTML 报告:
go tool cover -html=coverage.out -o coverage.html
在浏览器中打开,可以看到哪些代码被覆盖,哪些没有。
go test -coverpkg=./... -coverprofile=coverage.out ./...
go tool cover -func=coverage.out
.PHONY: test coverage
test:
go test -v ./...
coverage:
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
go tool cover -func=coverage.out
GitHub Actions 示例:
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Run tests
run: go test -v -coverprofile=coverage.out ./...
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: coverage.out
设置覆盖率门槛:
#!/bin/bash
coverage=$(go test -cover ./... 2>&1 | grep total | awk '{print $4}' | sed 's/%//')
if [ "$coverage" -lt 70 ]; then
echo "Coverage $coverage% is below threshold 70%"
exit 1
fi
echo "Coverage $coverage% passed"
func TestGetUser_EdgeCases(t *testing.T) {
tests := []struct {
name string
userID string
wantStatus int
}{
{"正常ID", "1", 200},
{"空ID", "", 400},
{"非数字ID", "abc", 400},
{"负数ID", "-1", 404},
{"超大ID", "999999999", 404},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 测试代码
})
}
}
func TestCreateUser_ErrorPaths(t *testing.T) {
t.Run("无效JSON", func(t *testing.T) {
// 测试无效 JSON
})
t.Run("验证失败", func(t *testing.T) {
// 测试验证失败
})
t.Run("数据库错误", func(t *testing.T) {
// 测试数据库错误
})
}
func TestWithMockDB(t *testing.T) {
mockDB := &MockDB{
Error: errors.New("connection failed"),
}
// 测试数据库错误场景
}
package main
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)
func TestUserHandlers(t *testing.T) {
gin.SetMode(gin.TestMode)
r := setupRouter()
t.Run("获取用户列表", func(t *testing.T) {
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/users", nil)
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
})
t.Run("创建用户", func(t *testing.T) {
body := `{"name":"张三","email":"test@example.com"}`
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/users", strings.NewReader(body))
req.Header.Set("Content-Type", "application/json")
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusCreated, w.Code)
})
t.Run("获取单个用户", func(t *testing.T) {
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/users/1", nil)
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
})
t.Run("删除用户", func(t *testing.T) {
w := httptest.NewRecorder()
req, _ := http.NewRequest("DELETE", "/users/1", nil)
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusNoContent, w.Code)
})
}
测试覆盖率是衡量测试质量的重要指标。使用 Go 内置工具生成覆盖率报告,集成到 CI/CD 中自动检查。覆盖率不是唯一标准,但可以作为参考。重点是测试关键路径和边界情况,而不是追求 100% 覆盖率。