Library / SDK
onsi/ginkgo avatar
onsi/ginkgo

Ginkgo is a spec tree bolted onto Go's testing package, and it takes your namespace with it

A Modern Testing Framework for Go

9,076 stars710 forksGoMIT

At a glance

What is it?
Ginkgo gives Go projects a container-based DSL for specs, parallel runs, labels and machine-readable reports, on top of the standard testing package. The trade is a dot-import that puts names like It and When into every file that uses it, and a Go 1.25 floor declared in go.mod.
Who is it for?
Ginkgo fits a team that writes integration suites large enough for the tree, labels and filtering to pay for themselves, and that accepts the dot-import and the Go 1.25 floor its go.mod declares. It does not fit a small library of table-driven unit tests, where Go's own testing package is already shorter.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 12 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • onsi/ginkgo9,076 ★ · +20
Star history
Repository2026-09-222026-11-14
onsi/ginkgo9.1k9.1k

20 Stars in the last 53 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Ginkgo runs inside Go's testing package and asks for the whole file namespace

Ginkgo is not a test runner that replaces Go's testing package. It builds on top of that foundation, which is why the module declares `go 1.25.0` and why the framework can sit beside existing table-driven tests in the same repository. Getting it into a project means two dot imports:

go
import (
    . "github.com/onsi/ginkgo/v2"
    . "github.com/onsi/gomega"
    ...
)

That is where the cost sits. A dot import puts every exported Ginkgo name directly into the importing file's scope, so Describe, Context, When, It, Specify, BeforeEach, AfterEach, BeforeSuite, AfterSuite and Label are all unqualified identifiers in your package from then on. Any helper of your own with one of those names now collides at compile time, and the collision is reported in the file you were editing rather than in the test that caused it. The version suffix matters too: the module path is github.com/onsi/ginkgo/v2, so two major lines can coexist in one build while you migrate.

`ginkgo -p` and `-randomize-all` change failure surfaces, not assertions

The framework can run specs in reproducibly random order and has support for parallelization, and the parallel case is one flag:

bash
ginkgo -p

The repository runs its own suite that way, with four flags at once:

bash
go run github.com/onsi/ginkgo/v2/ginkgo -r -p -randomize-all -keep-going

That combination is where suites stop being boring. Order dependence that never shows up on a laptop becomes visible the moment specs are shuffled or split across processes, because a spec that passed only because a neighbour had already warmed a cache now fails alone. Randomization is described as reproducible, which is what makes such a failure debuggable: once you have seen the order, you can reproduce it. `-keep-going` then changes the shape of a red run, since the run continues past a failure and reports a list rather than stopping at the first one. Labels and filter flags are the way back to a single spec; the docs cover combining filters on the command line and focusing specs programmatically.

`SpecContext` is the answer to a hung spec, and it is a pattern you have to follow

One of the stated goals is that you do not have to worry about a spec suite hanging or leaving a mess behind. The mechanism is a per-node `context.Context` plus the ability to interrupt a spec after a set period of time and then clean up. Both halves of that sentence are yours to honour. The context only helps if the code under test and the cleanup path actually take it, and the docs frame the pattern rather than enforce it, so a goroutine that ignores its context still outlives the spec that started it.

The payoff when you do follow it is real. A timeout becomes a reported failure with a name attached instead of a job that sits until the CI runner kills it, and the cleanup code has a cancellation signal to observe. The cost is that timeout handling becomes part of every spec that needs it, which in a large integration suite means a convention you have to review for, not a setting you set once. The sample spec shows the shape, with a BeforeEach that receives a SpecContext, which is where the deadline and the cancellation-aware cleanup belong.

The report formats you get come from pinned dependencies, not from a menu

Reporting is described as machine-readable output in a variety of formats, with the option to build custom reporting infrastructure programmatically. The repository tree shows where that lives: formatter/, reporters/, reporting_dsl.go and types/. The formats themselves are listed in the docs the README links to.

go.mod's direct requirements include github.com/joshdk/go-junit at v1.0.0, mfridman/tparse at v0.18.0, gkampitakis/go-snaps at v0.5.15 and google/pprof, each at a pinned version, and the indirect block pulls tidwall/gjson, tidwall/sjson, goccy/go-yaml and go.yaml.in/yaml/v3.

If your CI parses these reports, check the formats listed in the docs against what your pipeline expects.

`deprecated_dsl.go` sits beside `core_dsl.go`, so names are allowed to move

The root of the repository is a description of the public surface. There is core_dsl.go, decorator_dsl.go, ginkgo_t_dsl.go, helpergo_dsl.go, reporting_dsl.go and table_dsl.go, plus a dsl/ directory, and there is also deprecated_dsl.go sitting in the same directory as the live files. A file with that name is a promise that identifiers have been renamed and kept around for a while, and because the import is a dot import, you cannot alias your way around a rename: the old name is simply present until it is deleted.

Cadence is fast enough that this is a live concern. Recent tags include v2.34.0 on 2026-11-02, v2.33.1 on 2026-10-09 and v2.33.0 on 2026-09-17, and the last push to the repository landed on 2026-10-09. Two CHANGELOG.md and RELEASING.md files sit at the root, so the migration notes exist, but the detection mechanism for your codebase is the compiler. If a dot-imported identifier is removed, it shows up as undefined names across every spec file.

The Claude Code plugin teaches an agent the idioms, it does not check the specs

Ginkgo now ships a set of Claude Code skills as a plugin, and this repository doubles as the marketplace. From inside Claude Code:

code
/plugin marketplace add onsi/ginkgo
/plugin install ginkgo@ginkgo

The non-interactive form is `claude plugin marketplace add onsi/ginkgo` followed by `claude plugin install ginkgo@ginkgo`. That installs a family of `ginkgo:*` skills that activate automatically while you write and run specs, with `ginkgo:overview` as the starting point, and the full list lives in plugins/ginkgo/README.md. The tree backs this up: there is a .claude-plugin/ directory at the root and a plugins/ directory beside it.

What this changes is who writes the spec, not what proves it correct. The skills carry Ginkgo's idioms, decorators and known gotchas, which is exactly the vocabulary an agent lacks. They do not run your suite against a real dependency, and they cannot tell you that a Describe tree expresses the behaviour you meant rather than the behaviour you described. Treat generated specs as a starting draft that a human has to read for intent.

Container nodes put setup above the assertion, which is where failures get reported

The DSL is built from nestable container nodes: Describe, Context and When for organisation, BeforeEach and AfterEach for setup and cleanup, It and Specify as the subject nodes that hold assertions, and BeforeSuite and AfterSuite for suite-wide preparation and teardown. The sample spec shows the shape of a real one, with `var _ = Describe(...)` declared at package level, a BeforeEach building the library, the book and the user, and a When block that adds a nested BeforeEach receiving a SpecContext.

Read as a debugging story, this is the framework's main trade-off. Setup lives outside the assertion, so a single BeforeEach can prepare state for a whole branch of the tree, and every leaf under it inherits that work. The saving is real: no spec repeats its own setup. The cost is that a break in shared setup affects every spec beneath it, and a deep tree makes the distance between cause and reported failure longer than in a flat test. Suite-level nodes have the same property at a larger scale, so BeforeSuite work that any single spec needs pushes cost onto every spec in the run.

Editorial conclusion

Ginkgo fits a team that writes integration suites large enough for the tree, labels and filtering to pay for themselves, and that accepts the dot-import and the Go 1.25 floor its go.mod declares. It does not fit a small library of table-driven unit tests, where Go's own testing package is already shorter. Before adopting it, check whether your CI can consume the report formats you need, decide where the suite-level setup belongs between BeforeSuite and BeforeEach, and read CHANGELOG.md across a minor bump, since dot-imported names can move underneath you.

Frequently asked questions

What is Ginkgo used for in a Go project?

It is a testing framework that adds an expressive DSL on top of Go's testing package, with nestable Describe, Context and When containers, BeforeEach and AfterEach setup, and It or Specify nodes for assertions. It is complemented by the Gomega matcher library.

How do you install Ginkgo for a Go project?

Through the Go module toolchain: the module is github.com/onsi/ginkgo/v2 and its go.mod declares go 1.25.0. In code you bring it in with a dot import of github.com/onsi/ginkgo/v2 alongside github.com/onsi/gomega, and the CLI is run with go run github.com/onsi/ginkgo/v2/ginkgo.

How do you run a Ginkgo suite and control what runs?

The ginkgo command line tool supports generating, running, filtering and profiling suites, with ginkgo -p for parallel runs and ginkgo watch to rerun on change. Labels let you run subsets of specs, programmatically or on the command line, and filters can be combined.

Does Ginkgo need a different setup on macOS than on Linux?

The README does not split installation by operating system. What is stated is the toolchain requirement, since go.mod declares go 1.25.0, and the repository's own Makefile invokes the CLI through go run github.com/onsi/ginkgo/v2/ginkgo.

Official sources

  1. License: MIT
  2. onsi/ginkgo on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/onsi-ginkgo.svg)](https://hysenlabs.com/projects/onsi-ginkgo)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/onsi-ginkgo.svg)](https://hysenlabs.com/projects/onsi-ginkgo)

Was this useful?