Skip to content
Back to skills

Go Blackbox

ASecurity

Detect white box Go tests and propose conversion to black box tests (package foo_test). Internal skill used by golang-pro agent to enforce black box testing conventions.

  • 16 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 26, 2026
developmentgobashtestingrefactoringapi

Works with

  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add sgaunet/claude-plugins --skill go-blackbox --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Go Blackbox?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Go Blackbox
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/sgaunet-go-blackbox/badge)](https://www.skillsdirectory.com/skills/sgaunet-go-blackbox)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: go-blackbox
description: Detect white box Go tests and propose conversion to black box tests (package foo_test). Internal skill used by golang-pro agent to enforce black box testing conventions.
user-invocable: false
allowed-tools: Read, Glob, Grep, Bash(go test:*), Bash(go vet:*)
---

# Go Black Box Test Enforcement

Detect Go test files using white box testing (same package name) and propose conversion to black box testing (`package foo_test`). Creates `export_test.go` bridge files when internal symbols need external test access.

## When to Use

- Creating new test files (enforce black box from the start)
- Reviewing existing test files in a Go project
- User asks to audit or improve test quality
- The golang-pro agent encounters `*_test.go` files during implementation
- The golang-pro agent encounters goroutine-spawning code while writing or reviewing tests

## Prerequisites

1. **go.mod exists**: Abort if missing.
2. **Go source files exist**: At least one `.go` file and one `*_test.go` file to analyze.

## Workflow: Detect White Box Tests

### Step 1: Scan Test Files

Use Glob to find all test files:

```
**/*_test.go
```

Exclude `vendor/` and `export_test.go` files from analysis.

### Step 2: Classify Each Test File

For each `*_test.go` file, read the `package` declaration (first non-comment, non-blank line starting with `package`).

**Classification rules:**

| Package declaration | Source package | Classification |
|---|---|---|
| `package foo_test` | `foo` | Black box (correct) |
| `package foo` | `foo` | White box (needs conversion) |
| `package foo_test` | N/A (no source) | Black box (standalone) |

To determine the source package: look for non-test `.go` files in the same directory and read their `package` declaration.

### Step 3: Check for export_test.go

For each directory containing test files, check whether an `export_test.go` already exists.

### Step 4: Report Findings

Present a summary table:

| File | Package | Classification | export_test.go |
|---|---|---|---|
| `calc_test.go` | `calculator` | White box | No |
| `math_test.go` | `calculator_test` | Black box | N/A |
| `internal_test.go` | `parser` | White box | Yes |

## Workflow: Convert White Box to Black Box

For each white box test file identified:

### Step 1: Analyze Unexported Symbol Usage

Read the test file and identify references to unexported symbols (lowercase-initial identifiers) from the package under test:

- Unexported functions/methods called directly
- Unexported types used in variable declarations or assertions
- Unexported struct fields accessed directly
- Unexported constants/variables referenced

### Step 2: Determine Conversion Strategy

**Case A — No unexported symbols used:**
- Change `package foo` to `package foo_test`
- Add import for the package under test
- Prefix exported symbol references with the package name (e.g., `Add(1, 2)` → `calculator.Add(1, 2)`)

**Case B — Unexported symbols used:**
- Change `package foo` to `package foo_test` in the test file
- Add import for the package under test
- Create or update `export_test.go` in the same directory with `package foo` (no `_test` suffix) that exports needed internals:

```go
package foo

// Exported for testing in foo_test package.
var (
    TestableInternalFunc = internalFunc
    TestableInternalVar  = internalVar
)
```

- Update test file to use the exported aliases via the package import

**Case C — Heavy internal coupling (>10 unexported references):**
- Flag for manual review rather than automatic conversion
- Suggest refactoring the API to export what tests need, or keeping this specific file as white box with a justifying comment

### Step 3: Validate

After conversion, run:

```bash
go test ./...
go vet ./...
```

If tests fail, diagnose: missing imports, renamed references, or unexported symbols not yet bridged via `export_test.go`.

## Workflow: Add Goroutine Leak Detection

After conversion (or during new test creation), detect test functions that exercise goroutine-spawning code and add `go.uber.org/goleak` verification.

### Step 1: Identify Goroutine-Spawning Source Code

For each package under test, scan the **non-test** `.go` source files for goroutine launches:

```
Grep pattern: `go\s+func\s*\(|go\s+[a-zA-Z]`
```

This matches:
- `go func() { ... }()` — anonymous goroutine
- `go myFunction(...)` — named function goroutine
- `go obj.Method(...)` — method goroutine

Exclude `vendor/` and generated files. Record which **exported functions/methods** directly or transitively spawn goroutines.

### Step 2: Map Tests to Goroutine-Spawning Functions

For each test file in the package, identify test functions that call (directly or via helpers) the goroutine-spawning functions found in Step 1.

**Classification:**

| Test Function | Calls Goroutine-Spawning Code | Action |
|---|---|---|
| `TestWorkerPool` | Yes (`StartWorkers` uses `go`) | Add `goleak.VerifyNone(t)` |
| `TestParseConfig` | No | Skip — no goroutine risk |
| `TestServerStart` | Yes (`Serve` uses `go`) | Add `goleak.VerifyNone(t)` |

### Step 3: Add goleak to Test Functions

For each test function classified as needing leak detection:

1. **Add the defer call** as the first statement in the test function body:

```go
func TestWorkerPool(t *testing.T) {
    defer goleak.VerifyNone(t)
    // ... rest of test
}
```

2. **Add the import** to the test file if not already present:

```go
import (
    "testing"

    "go.uber.org/goleak"
)
```

3. **Install the module** if not already in `go.mod`:

```bash
go get go.uber.org/goleak@latest
go mod tidy
```

### Step 4: Handle goleak Options

Some goroutines are expected and should not trigger false positives. Common cases:

| Scenario | Option |
|---|---|
| Known background goroutine from a dependency | `goleak.IgnoreTopFunction("package.function")` |
| Goroutine matching a pattern | `goleak.IgnoreAnyFunction("package.glob*")` |

If `goleak.VerifyNone(t)` fails due to known background goroutines (e.g., from `net/http` or logging libraries), add options:

```go
defer goleak.VerifyNone(t,
    goleak.IgnoreTopFunction("net/http.(*Server).Serve"),
)
```

Do not suppress unknown goroutines — investigate them first.

### Step 5: Validate

Run the tests with the race detector to confirm no leaks and no regressions:

```bash
go test -race -count=1 ./...
```

If `goleak.VerifyNone` reports leaked goroutines:
1. Check if the tested code properly shuts down goroutines (context cancellation, channel close, `sync.WaitGroup`)
2. If the goroutine is from a third-party dependency and cannot be controlled, add a targeted `goleak.IgnoreTopFunction` option
3. Never blanket-ignore all goroutines — each ignore must be specific and documented

## export_test.go Conventions

1. **File name**: Always `export_test.go` (Go convention)
2. **Package**: Same as the source package (NOT `_test` suffix) — this is what makes it a bridge
3. **Build constraint**: None needed — Go includes `export_test.go` only during `go test`
4. **Naming**: Prefix exported aliases with `Testable` or use the capitalized form of the original name
5. **Comment**: Add `// Exported for testing in <pkg>_test package.` header comment
6. **Scope**: Export only what tests actually need — do not blanket-export all internals

## Error Handling

| Condition | Action |
|-----------|--------|
| No `go.mod` found | Abort: "Not a Go module." |
| No test files found | Report: "No test files found. Nothing to analyze." |
| All tests already black box | Report: "All test files use black box naming. No conversion needed." |
| Test compilation fails after conversion | Diagnose missing imports or unexported references. Attempt fix (max 2 retries). |
| `go vet` fails after conversion | Display errors and suggest manual review. |
| `go get go.uber.org/goleak` fails | Display error. Check network connectivity and module proxy settings. |
| `goleak.VerifyNone` reports leaked goroutines | Investigate source. Fix goroutine lifecycle first; use `IgnoreTopFunction` only for third-party goroutines that cannot be controlled. |
| No goroutine-spawning code found in package | Skip goleak workflow: "No goroutine usage detected. Goroutine leak detection not needed." |

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.