Token导航 LogoToken导航

Agent Skill · 开发

go-test-table-driven(去测试表驱动)

用于辅助测试设计、自动化测试、用例整理和回归验证。它适合让 Agent 编写单元测试、端到端测试、测试计划或根据失败日志定位问题。使用时需要确认项目测试框架、运行命令和夹具数据,避免为了通过测试而改坏真实逻辑;涉及浏览器或外部服务时,应区分本地模拟、测试环境和生产环境。

go-test-table-driven 是开发类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为权限未标注;许可证信息还不明确,第三方扫描已通过,按安装量在开发类的 21,746 个 Skill 里排第 13,622。

总安装 861周安装 37GitHub Stars 56下载 302核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:go-test-table-driven(去测试表驱动)
来源仓库:https://github.com/eduardo-sl/go-agent-skills
仓库路径:skills/go-test-table-driven
安装命令:
npx skills add https://github.com/eduardo-sl/go-agent-skills --skill go-test-table-driven
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/eduardo-sl/go-agent-skills --skill go-test-table-driven

这个 Skill 是做什么的

开发类 Skill 面向写代码、改代码和排查问题,一般会读写仓库文件,部分还会执行构建或测试命令。

go-test-table-driven 用于辅助测试设计、自动化测试、用例整理和回归验证。适合让 Agent 编写单元测试、端到端测试或根据失败日志定位问题。使用时需确认项目测试框架、运行命令和夹具数据,避免误改逻辑。涉及浏览器或外部服务时,应区分本地模拟与生产环境。

适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

来源仓库内路径:skills/go-test-table-driven

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/eduardo-sl/go-agent-skills --skill go-test-tab…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    权限没有明确标注,第一次使用时只给一个无关紧要的任务,观察它到底读了哪些文件和网络。

常见问题

6 条

go-test-table-driven 怎么安装?

在终端执行 npx skills add https://github.com/eduardo-sl/go-agent-skills --skill go-test-table-driven。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

go-test-table-driven 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

go-test-table-driven 需要什么权限?

权限级别没有明确标注。第一次使用时建议只给无关紧要的任务,观察它实际读写了哪些内容。

go-test-table-driven 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

go-test-table-driven 用的人多吗?

公开数据是累计安装 861 次,近一周安装 37 次,下载 302 次,GitHub 56 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

go-test-table-driven 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理开发类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

开发类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
skill-vetter

技能审查员 · OpenClaw

8,779,018986操作浏览器MIT-0通过
ontology

本体论 · OpenClaw

6,636,960576只读MIT-0通过
entra-app-registration

内部应用程序注册 · Codex、Claude

6,631,872764接触敏感数据未明确通过
entra-app-registration

内部应用程序注册 · Codex、Claude

3,757,680201接触敏感数据未明确通过
nano-banana-pro

纳米香蕉专业版 · OpenClaw

3,526,898364接触敏感数据MIT-0有提醒
go-test-table-driven

去测试表驱动 · 本页

86156权限未标注未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Go Table-Driven Tests

Table-driven tests are a powerful Go idiom — when used correctly. The problem is that most codebases either underuse them (writing 10 copy-paste tests) or overuse them (jamming complex branching logic into a 200-line struct). This skill covers the sweet spot.

1. When Table-Driven Tests Shine

Use table tests when ALL of these are true:

  • Same function under test across all cases
  • Same assertion pattern — input goes in, output comes out, compare
  • Cases differ only in data, not in setup or verification logic
  • 3+ cases — fewer than 3, explicit subtests are clearer

The canonical use case: pure functions, parsers, validators, formatters.

func TestFormatCurrency(t *testing.T) {
    tests := []struct {
        name     string
        cents    int64
        currency string
        want     string
    }{
        {
            name:     "USD whole dollars",
            cents:    1000,
            currency: "USD",
            want:     "$10.00",
        },
        {
            name:     "USD with cents",
            cents:    1050,
            currency: "USD",
            want:     "$10.50",
        },
        {
            name:     "EUR formatting",
            cents:    999,
            currency: "EUR",
            want:     "€9.99",
        },
        {
            name:     "zero amount",
            cents:    0,
            currency: "USD",
            want:     "$0.00",
        },
        {
            name:     "negative amount",
            cents:    -500,
            currency: "USD",
            want:     "-$5.00",
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got := FormatCurrency(tt.cents, tt.currency)
            assert.Equal(t, tt.want, got)
        })
    }
}

Why this works: every case has the same shape, the loop body is 2 lines, and adding a new case is one struct literal. No branching, no conditionals.

2. When NOT to Use Table-Driven Tests

Complex per-case setup

If each case needs different mocks, different state, or different dependencies:

// ❌ Bad — table test with branching setup
tests := []struct {
    name        string
    setupMock   func(*mockStore)     // each case wires differently
    setupAuth   func(*mockAuth)      // more per-case wiring
    input       Request
    wantStatus  int
    shouldNotify bool                // branching assertion
}{...}

for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        store := &mockStore{}
        tt.setupMock(store)          // hiding logic inside functions
        auth := &mockAuth{}
        tt.setupAuth(auth)
        // ... 20 lines of conditional assertions
    })
}

This is a code smell. The table is just hiding complexity behind function fields. Write separate subtests instead — they're longer but honest:

// ✅ Good — explicit subtests for different scenarios
func TestOrderHandler_Create(t *testing.T) {
    t.Run("succeeds with valid order", func(t *testing.T) {
        store := &mockStore{createFunc: func(...) (*Order, error) {
            return &Order{ID: "1"}, nil
        }}
        handler := NewHandler(store)
        // ... clear, readable, self-contained
    })

    t.Run("returns 401 when unauthenticated", func(t *testing.T) {
        handler := NewHandler(&mockStore{})
        // ... different setup, different assertions
    })
}

Fewer than 3 cases

Two cases don't need a table. The overhead of defining the struct is more code than just writing two tests:

// ❌ Overkill for 2 cases
tests := []struct {
    name    string
    input   string
    wantErr bool
}{
    {"valid", "hello", false},
    {"empty", "", true},
}

// ✅ Just write them
func TestValidate_AcceptsNonEmptyString(t *testing.T) {
    require.NoError(t, Validate("hello"))
}

func TestValidate_RejectsEmptyString(t *testing.T) {
    require.Error(t, Validate(""))
}

Multiple branching paths

If your loop body has if tt.shouldError / if tt.expectNotification / if tt.wantRedirect — you've outgrown the table. Each branch is a different test pretending to share a structure.

3. Struct Design

Keep fields minimal

Every field should change between at least 2 cases. If a field has the same value in all cases, it's not a variable — it's setup:

// ❌ Bad — userRole is "admin" in every case
tests := []struct {
    name     string
    userRole string  // always "admin"
    input    string
    want     string
}{
    {"case1", "admin", "a", "A"},
    {"case2", "admin", "b", "B"},
}

// ✅ Good — remove constants from the struct
func TestAdminFormatter(t *testing.T) {
    ctx := contextWithRole("admin") // shared setup, outside table

    tests := []struct {
        name  string
        input string
        want  string
    }{
        {"case1", "a", "A"},
        {"case2", "b", "B"},
    }
    // ...
}

Name the name field well

The name field appears in test output. Make it a short sentence that explains the scenario, not a label:

// ✅ Good names
{name: "trims leading whitespace"},
{name: "returns error for negative amount"},
{name: "handles unicode characters"},

// ❌ Bad names
{name: "case1"},
{name: "success"},
{name: "test with special chars"},

Use wantErr correctly

// ✅ Good — simple boolean for "should it error?"
tests := []struct {
    name    string
    input   string
    want    int
    wantErr bool
}{
    {name: "valid number", input: "42", want: 42},
    {name: "empty string", input: "", wantErr: true},
    {name: "not a number", input: "abc", wantErr: true},
}

for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        got, err := ParseInt(tt.input)
        if tt.wantErr {
            require.Error(t, err)
            return
        }
        require.NoError(t, err)
        assert.Equal(t, tt.want, got)
    })
}

When you need to check specific errors

Use a wantErrIs field with a sentinel error, not just a boolean:

tests := []struct {
    name      string
    id        string
    wantErrIs error // nil means no error expected
}{
    {name: "valid id", id: "123"},
    {name: "empty id", id: "", wantErrIs: ErrInvalidID},
    {name: "not found", id: "999", wantErrIs: ErrNotFound},
}

for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        _, err := store.GetByID(ctx, tt.id)
        if tt.wantErrIs != nil {
            require.ErrorIs(t, err, tt.wantErrIs)
            return
        }
        require.NoError(t, err)
    })
}

4. The Loop Body Must Be Trivial

The entire point of a table test is that the execution logic is identical for every case. If your loop body exceeds ~10 lines, something is wrong.

// ✅ Good — loop body is 5 lines
for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        got, err := Process(tt.input)
        require.NoError(t, err)
        assert.Equal(t, tt.want, got)
    })
}

// ❌ Bad — loop body has become a mini-program
for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        if tt.setupDB {
            db := setupDB(t)
            defer db.Close()
        }
        svc := NewService()
        if tt.withCache { svc.EnableCache() }
        got, err := svc.Process(tt.input)
        if tt.wantErr {
            require.Error(t, err)
            if tt.wantErrMsg != "" { assert.Contains(t, err.Error(), tt.wantErrMsg) }
            return
        }
        // ... more conditionals, more branches
    })
}

If you see this, split into separate subtests or separate test functions.

5. Parallel Table Tests

for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        t.Parallel()
        got := Transform(tt.input)
        assert.Equal(t, tt.want, got)
    })
}

In Go 1.22+, the loop variable is scoped per iteration, so the old tt:= tt capture is unnecessary. For Go <1.22, you still need it:

// Go <1.22 only
for _, tt := range tests {
    tt := tt // capture range variable
    t.Run(tt.name, func(t *testing.T) {
        t.Parallel()
        // ...
    })
}

Only use t.Parallel() in table tests when the function under test has no side effects and no shared mutable state.

6. Readability Tricks

Align struct literals for scanning

tests := []struct {
    name  string
    input string
    want  string
}{
    {"lowercase",           "hello",       "hello"},
    {"uppercase",           "HELLO",       "hello"},
    {"mixed case",          "HeLLo",       "hello"},
    {"with spaces",         "Hello World", "hello world"},
    {"already lowercase",   "test",        "test"},
}

This works for simple cases. For complex structs, use the multi-line format:

tests := []struct {
    name   string
    config Config
    want   string
}{
    {
        name: "default timeout",
        config: Config{
            Host:    "localhost",
            Timeout: 0, // should get default
        },
        want: "localhost:8080",
    },
    {
        name: "custom port",
        config: Config{
            Host: "localhost",
            Port: 9090,
        },
        want: "localhost:9090",
    },
}

Map-based tables for ultra-simple cases

When the struct would just be {name, input, want}:

func TestStatusText(t *testing.T) {
    cases := map[string]struct {
        code int
        want string
    }{
        "ok":          {200, "OK"},
        "not found":   {404, "Not Found"},
        "server error": {500, "Internal Server Error"},
    }

    for name, tc := range cases {
        t.Run(name, func(t *testing.T) {
            assert.Equal(t, tc.want, StatusText(tc.code))
        })
    }
}

Note: map iteration order is random, so this also stress-tests that your cases are truly independent.

7. Error-Only Tables

When you're testing a validator and only care about which inputs fail:

func TestValidateEmail(t *testing.T) {
    valid := []string{
        "user@example.com",
        "user+tag@example.com",
        "user@sub.domain.com",
    }
    for _, email := range valid {
        t.Run("valid/"+email, func(t *testing.T) {
            require.NoError(t, ValidateEmail(email))
        })
    }

    invalid := []string{
        "",
        "@",
        "user@",
        "@domain.com",
        "user space@example.com",
    }
    for _, email := range invalid {
        t.Run("invalid/"+email, func(t *testing.T) {
            require.Error(t, ValidateEmail(email))
        })
    }
}

Two simple slices. No struct needed. The test name includes the input value, so failures are self-documenting.

8. Refactoring Bloated Tables

Signs your table test needs refactoring:

SymptomFix
Struct has 8+ fieldsSplit into multiple test functions by scenario
setupFunc field in structExtract to separate subtests with explicit setup
if tt.shouldX in loop bodyEach branch is a different test — split it
Same 3 fields are identical in every caseMove to shared setup outside the table
Test name is the only way to understand the caseThe case is too complex for a table
Adding a case requires understanding all other casesTable has grown beyond its useful life

Decision Flowchart

  1. Is the function pure (input → output, no side effects)? Yes → table test is probably ideal. Go to 2. No → consider explicit subtests first.
  2. Do all cases share the exact same assertion pattern? Yes → table test. Go to 3. No → explicit subtests.
  3. Can each case be expressed in ≤5 struct fields? Yes → table test. No → split by scenario into separate test functions.
  4. Is the loop body ≤10 lines? Yes → you're golden. No → the table is hiding complexity. Refactor.

Verification Checklist

  1. Table struct has only fields that vary between cases
  2. Every case has a descriptive name field
  3. Loop body is ≤10 lines with no branching
  4. No setupFunc or mockFunc fields in the struct
  5. wantErr is a simple bool or sentinel, not a string match
  6. Cases cover: happy path, error path, edge cases (empty, nil, zero, max)
  7. t.Run wraps each case for named subtests
  8. t.Parallel() used only when function is side-effect-free

平台分布

Codex

35.94%
按下载量换算109

Claude

31.67%
按下载量换算96

Cursor

18.73%
按下载量换算57

Gemini CLI

9.98%
按下载量换算30

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

权限未标注

当前来源未能明确判断权限范围,默认进入异常复核队列。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills