
Writing Specs
Author good Ginkgo specs, container nodes (Describe/Context/When), subject nodes...
Install
npx skills add onsi/ginkgo --skill writing-specs -g -a claude-code -y- Repository
- onsi/ginkgo9.1k
- Category
- Testing
- Updated
- 26d ago
- License
- MIT
- Context cost
- Mediumabout 1,841 tokens
- Runs scripts
- No, instructions only
What it does
Author good Ginkgo specs, container nodes (Describe/Context/When), subject nodes (It/Specify), setup/cleanup nodes (BeforeEach, JustBeforeEach, AfterEach, DeferCleanup, BeforeSuite/AfterSuite), the "declare in container, initialize in BeforeEach" rule, separating creation from configuration, reusable test helpers with GinkgoHelper()/GinkgoHelperGo(), and By/GinkgoWriter output. Use when writing or reviewing specs or extracting a test helper. Covers the tree-construction-time pitfalls (no assertions/init/loop-capture in container bodies).
Writing Ginkgo specs
Assumes Ginkgo is wired into the suite (ginkgo:setup) and you know the two-phase model (ginkgo:overview). Docs: https://onsi.github.io/ginkgo/#writing-specs.
The shape of a spec
var _ = Describe("Books", func() {
var book *books.Book // declare here...
BeforeEach(func() {
book = &books.Book{ // ...initialize here, fresh per spec
Title: "Les Miserables",
Author: "Victor Hugo",
Pages: 2783,
}
Expect(book.IsValid()).To(BeTrue()) // assertions in setup are fine
})
Describe("categorizing", func() {
Context("with more than 300 pages", func() {
It("is a novel", func() {
Expect(book.Category()).To(Equal(books.CategoryNovel))
})
})
})
})
- Containers (
Describe/Context/When) organize; they're identical, pick the one that reads as a sentence. Subjects (It/Specify) hold the assertions; one spec runs perIt. - The spec's full name is the concatenation of every container text plus the
Ittext, write them to read as a phrase.
The rule that prevents most bugs: declare in container, initialize in setup
Container bodies run once, at tree-construction time (ginkgo:overview). So:
- Declare shared variables in the container body (
var book *books.Book). - Initialize them in
BeforeEachso each spec gets a clean copy.
// WRONG, runs once at construction; every spec shares & pollutes one book
var _ = Describe("Books", func() {
book := &books.Book{Pages: 2783} // initialized at construction time
It("mutates", func() { book.Pages = 0 })
It("expects 2783", func() { Expect(book.Pages).To(Equal(2783)) }) // flaky under randomization
})
The same trap explains: no assertions in container bodies (they fire at construction, with no spec active) and no expensive/stateful work in container bodies. If you see logic directly inside a Describe/Context/When body, it almost always belongs in a BeforeEach.


