ginkgo-tests
Configures and runs Ginkgo - Go BDD test framework with `Describe` / `Context` / `It` nesting; `BeforeEach` / `AfterEach` / `JustBeforeEach` / `JustAfterEach` lifecycle; Gomega matchers DSL (`Expect(actual).To(Equal(expected))`); parallel execution via `-p`; focus (`F` prefix) + skip (`P` prefix); `DescribeTable` + `Entry` for parametrized tests; `ginkgo` CLI tool. Use when working with Go on a BDD-style test suite (Kubernetes-ecosystem standard).
Install with skills.sh (any agent)
npx skills add testland/qa --skill ginkgo-testsginkgo-tests
Overview
Per onsi.github.io/ginkgo (opens in new window):
Ginkgo is a Go BDD framework. The Kubernetes ecosystem standardized on Ginkgo + Gomega for its test suites.
For non-BDD Go projects, go-test (stdlib) is the idiomatic choice. Ginkgo fits when:
Step 1 - Install
go install github.com/onsi/ginkgo/v2/ginkgo@latest
go get github.com/onsi/ginkgo/v2
go get github.com/onsi/gomega/...Step 2 - Bootstrap
In the package to test:
ginkgo bootstrap # creates <package>_suite_test.go
ginkgo generate calc # creates calc_test.go template<package>_suite_test.go:
package calc_test
import (
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestCalc(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Calc Suite")
}Step 3 - Spec structure
package calc_test
import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"myproject/calc"
)
var _ = Describe("Calculator", func() {
var c *calc.Calculator
BeforeEach(func() {
c = calc.New()
})
Describe("Add", func() {
Context("with positive numbers", func() {
It("adds correctly", func() {
Expect(c.Add(1, 2)).To(Equal(3))
})
})
Context("with overflow", func() {
It("returns error", func() {
_, err := c.AddSafe(math.MaxInt, 1)
Expect(err).To(HaveOccurred())
})
})
})
})The var _ = Describe(...) pattern registers the spec at package init time. Ginkgo discovers + runs registered specs.
Step 4 - Lifecycle hooks
Per gn-docs (opens in new window):
BeforeSuite(func() { /* once before all specs */ })
AfterSuite(func() { /* once after all specs */ })
BeforeEach(func() { /* before each It in scope */ })
AfterEach(func() { /* after each It in scope */ })
JustBeforeEach(func() { /* after BeforeEach but before It */ })
JustAfterEach(func() { /* after It but before AfterEach */ })Hooks nest with Describe/Context - BeforeEach in nested Context runs in addition to outer BeforeEach.
Step 5 - Gomega matchers
Per onsi.github.io/gomega (opens in new window):
Expect(value).To(Equal(expected))
Expect(err).To(HaveOccurred())
Expect(list).To(HaveLen(3))
Expect(value).To(BeNumerically(">", 0))
Eventually(func() bool { return ready() }).Should(BeTrue())Eventually polls until the condition holds; Consistently polls to verify it stays true. Useful for async + concurrent code.
Full matcher DSL (strings, collections, numeric tolerance, panics, errors, channels): references/gomega.md.
Step 6 - DescribeTable + Entry
DescribeTable("Add",
func(a, b, expected int) {
Expect(calc.Add(a, b)).To(Equal(expected))
},
Entry("positive", 1, 2, 3),
Entry("zero", 0, 0, 0),
Entry("negative", -1, 1, 0),
Entry("large", 100, 200, 300),
)DescribeTable is Ginkgo's parametrize equivalent of pytest's @pytest.mark.parametrize or JUnit's @ParameterizedTest.
Step 7 - Focus + skip
FDescribe("focused", func() { ... }) // F prefix: only this runs
PDescribe("pending", func() { ... }) // P prefix: skippedSame for FContext/PContext, FIt/PIt, FDescribeTable/PDescribeTable.
CI gating: lint forbid F prefixes via ginkgo --no-focus (errors if any focused specs in suite).
Step 8 - Parallel execution
ginkgo -p ./... # parallel; uses CPU count
ginkgo -procs=4 ./... # explicit process countEach parallel process runs a subset of specs. Tests must be independent - shared state breaks parallel runs.
Step 9 - CI integration
ginkgo -p --cover --coverprofile=coverage.out --no-focus -r--no-focus fails the build if any F-prefix specs exist (catches debug-leftover focus).
Full GitHub Actions workflow (coverage upload, JUnit XML report): references/ci.md.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Use Ginkgo for non-BDD codebase | Verbose vs stdlib testing | Use go-test for non-BDD |
Commit FDescribe / FIt accidentally | Suite runs only focused specs | --no-focus flag (Step 9) |
Skip Eventually for async assertions | Sleep-based polls are flaky | Use Eventually/Consistently (Step 5) |
| Heavy nesting (5+ levels) | Test setup hard to reason about | Flatten with Describe+It |
Use . import for both Ginkgo + Gomega without thinking | Namespace pollution | Standard convention; document |
Limitations
References
Ginkgo CI integration
View source (opens in new window)Ginkgo CI integration
Per Ginkgo documentation (opens in new window).
GitHub Actions with coverage
- run: go install github.com/onsi/ginkgo/v2/ginkgo@latest
- run: ginkgo -p --cover --coverprofile=coverage.out --no-focus -r
- uses: codecov/codecov-action@v4
with: { files: coverage.out }JUnit XML output
For CI systems that ingest JUnit test reports:
ginkgo --junit-report=junit.xml -rThis writes a JUnit-format report that most CI dashboards render as a per-spec pass/fail table.
Gating on focused specs
ginkgo --no-focus errors if the suite contains any focused (F-prefix) specs. Wire it into the CI run so an accidentally committed FDescribe or FIt fails the pipeline instead of silently skipping the rest of the suite.
Gomega matchers reference
View source (opens in new window)Gomega matchers reference
Per Gomega documentation (opens in new window).
Expect(actual).To(matcher) and its negation Expect(actual).NotTo(matcher) are the core assertion forms.
Equality and nil
Expect(value).To(Equal(expected))
Expect(value).NotTo(Equal(unexpected))
Expect(value).To(BeNil())
Expect(value).To(BeTrue())Strings
Expect(str).To(ContainSubstring("substring"))
Expect(str).To(MatchRegexp(`\d+`))Collections
Expect(list).To(HaveLen(3))
Expect(list).To(ContainElement("alice"))
Expect(list).To(ConsistOf("alice", "bob")) // unorderedNumeric
Expect(value).To(BeNumerically(">", 0))
Expect(value).To(BeNumerically("~", 3.14, 0.01)) // within tolerancePanics, errors, channels
Expect(action).To(Panic())
Expect(err).To(MatchError("expected message"))
Expect(channel).To(Receive(&value))Async polling
Eventually(func() bool { return ready() }).Should(BeTrue())
Consistently(func() bool { return stable() }).Should(BeTrue())Eventually polls until the condition holds; Consistently polls to verify it stays true. Both suit async and concurrent code where a value settles over time rather than immediately.
Related skills
cargo-test
Configures and runs Rust's built-in `cargo test` - `#[test]` + `#[should_panic]` + `Result<(), E>` returns; integration tests in `tests/`; doc-tests embedded in `///` comments; `--lib` / `--bins` / `--all-targets` / `--workspace` selection; `cargo bench` (nightly) + Criterion (stable); `cargo test -- --test-threads=1` for serial; `cargo test -- --nocapture` to see println output. Use for any Rust project - testing is built into Cargo, no install needed.
go-rust-mocking
Isolates dependencies in Go and Rust unit tests using test-double generation - Go: `go.uber.org/mock` (gomock + mockgen codegen) and `github.com/stretchr/testify/mock` (hand-written stubs); Rust: `mockall` crate with `#[automock]` for traits and `mock!` macro for structs and external traits. Use when a unit test reaches a database, HTTP client, file system, or any interface boundary that must be replaced with a controlled fake to keep tests fast, deterministic, and isolated.
go-test
Configures and runs Go's stdlib `testing` package - `func TestXxx(t *testing.T)` convention; table-driven tests via slice + range; `t.Run` for subtests with hierarchical names; `t.Parallel()` for parallel execution; benchmarks (`func BenchmarkXxx(b *testing.B)`); examples (`func ExampleXxx()`); fuzzing (`func FuzzXxx(f *testing.F)`); coverage via `go test -cover`/`-coverprofile`; build tags for selective compilation. Use for any Go project - testing is a stdlib feature, no install needed.
rstest-tests
Configures and runs rstest - Rust parametrized + fixture-based testing crate; `#[rstest]` attribute + `#[case(...)]` for parametrize; `#[fixture]` for reusable test setup; matrix tests via multiple `#[case]` × N (cartesian product); async test support via `#[async_std::test]` / `#[tokio::test]` + `#[rstest]`; `#[future]` for async fixtures. Use when working with Rust and needing parametrize/fixture patterns beyond stdlib `#[test]`.