When Code Becomes a Story
\nImagine: you write code in Go, run the tests — a green bar. But the manager looks at your t.Errorf and doesn't understand what exactly broke. The business says: "Make it so the user can register," and the developer translates this into "POST /api/v1/user with email validation." The gap between technical and human language is the main headache of many projects.
There is a solution — BDD (Behaviour-Driven Development) in Go. This is not just tests. It is a bridge between code and business logic. In this article, we will break down how to write BDD tests in Go using real examples, and why your product manager will thank you.
\n\nWhat is BDD and Why Go?
\nBehaviour-Driven Development is an evolution of TDD. If TDD focuses on "how" (how to implement a function), then BDD answers "what" (what the system should do from the user's perspective).
\nGo is ideal for BDD thanks to its simplicity, the built-in testing package, and powerful frameworks like Ginkgo and Godog. The Go community actively uses BDD for microservices, APIs, and even CLI utilities.
Key Principles of BDD in Go
\n- \n
- Ubiquitous Language — scenarios are written in English or Russian that the business understands. \n
- Given-When-Then — the structure of any scenario: preconditions → action → expected result. \n
- Automation — scenarios are turned into executable Go tests. \n
Tools for BDD in Go
\nThe choice of framework depends on how you want to describe scenarios. Here are two main approaches:
\n\n1. Ginkgo + Gomega (for Go Developers)
\nGinkgo is a BDD framework written in pure Go. Scenarios look like Go code with readable descriptions. Gomega is a matcher library for assertions.
\nPros: full control over code, excellent integration with Go tooling, support for asynchronous tests.
\n\n2. Godog (Cucumber for Go)
\nGodog is a Cucumber implementation for Go. You write scenarios in Gherkin (Feature files in natural language), and then implement the steps in Go.
\nPros: scenarios are readable even by non-programmers, easy to change logic without editing code.
\n\nPractice: Writing a BDD Test in Go with Ginkgo
\nLet's create a simple service — an online store shopping cart. We'll write a BDD test that checks adding an item.
\n\nStep 1: Installing Ginkgo
\ngo get github.com/onsi/ginkgo/v2\ngo get github.com/onsi/gomega\n\nStep 2: Initializing the Test Package
\ncd mycart\nginkgo bootstrap\nA file mycart_suite_test.go is created.
Step 3: Writing the Scenario
\nLet's create a file cart_test.go:
package mycart_test\n\nimport (\n . "github.com/onsi/ginkgo/v2"\n . "github.com/onsi/gomega"\n "mycart"\n)\n\nvar _ = Describe("Cart", func() {\n var cart *mycart.Cart\n\n BeforeEach(func() {\n cart = mycart.NewCart()\n })\n\n Describe("Adding an item", func() {\n Context("with a correct ID and price", func() {\n It("should increase the number of items by 1", func() {\n cart.AddItem("sku-123", 100.0)\n Expect(cart.TotalItems()).To(Equal(1))\n })\n\n It("should correctly calculate the total sum", func() {\n cart.AddItem("sku-123", 100.0)\n cart.AddItem("sku-456", 250.0)\n Expect(cart.TotalPrice()).To(BeNumerically("~", 350.0, 0.01))\n })\n })\n\n Context("with an item that already exists", func() {\n It("should increase the quantity, not add a new item", func() {\n cart.