Unit testing in Go with Ginkgo: Part 1

Mark St. Godard
Apr 3 · 4 min read

At Bold, many of our back-end services are written in Go. On any given day, over 80,000 eCommerce websites use our software to power their online sales. Any downtime or severe bugs could result in lost sales and revenue for these merchants, which is why it’s important that we focus on delivering high-quality production code.

Test-driven development (TDD) is an important practice in our software development process. It helps give us confidence that the changes we’re introducing won’t cause any unintended side effects.

Go has a fairly capable built-in testing package, however it does have some limitations. This article will explain (with some simple examples) how we use Ginkgo and Gomega at Bold to write more expressive and structured unit tests in Go.

Background

Let’s first take a look at Go’s testing package and toolchain:

For example, I have a package adder which provides an Add function that sums two integers.

Using the testing package, a test could be written like this:

Run the test in your terminal with go test.

$ go test PASS ok      
github.com/markstgodard/adder 0.006s

Great, a passing unit test, but what if my function has a bug and subtracts instead of adds? What would the output of a go test look like?

$ go test 
--- FAIL: TestAdd (0.00s)
adder_test.go:8: Sum, got: -1, want: 5.
FAIL
exit status 1
FAIL github.com/markstgodard/adder 0.006s

This is fine for basic use, but the built-in Go testing package has two major limitations:

  • It only provides a simple XUnit style of tests

Enter Ginkgo 🎉

Ginkgo is a Go unit testing framework, created by Onsi Fakhouri, that provides for a BDD style of unit tests (think RSpec, but for Go).

Getting started

To get started with Ginkgo, follow these steps.

Download Ginkgo

Install the latest version of ginkgo with go get.

$ go get -u github.com/onsi/ginkgo/ginkgo 
$ go get -u github.com/onsi/gomega/...

Create a Suite

Ginkgo requires a test suite for each package that your program uses.

$ mkdir adder && cd adder
$ ginkgo bootstrap

Generating ginkgo test suite bootstrap for adder in:
adder_suite_test.go

This will create a test suite to bootstrap running your tests.

Create a new unit test

Now that we have a suite, let’s create our first Ginkgo unit test!

$ ginkgo generate 
Generating ginkgo test for Adder in:
adder_test.go

This will generate an empty test file, into which you can start adding individual unit tests.

Now, let’s update and add our first test:

Running unit tests

To run our unit tests, we run Ginkgo.

$ ginkgo 
Running Suite: Adder Suite
==========================
Random Seed: 1552860025
Will run 1 of 1 specs

Ran 1 of 1 Specs in 0.000 seconds
SUCCESS! -- 1 Passed | 0 Failed | 0 Pending | 0 Skipped
PASS
Ginkgo ran 1 suite in 1.549965067s
Test Suite Passed

Now, let’s say our requirements change and we’re only allowed to add positive numbers. Using the TDD process, let’s start by adding a failing test.

We can use Context to indicate specific pre-conditions, such as when our inputs are valid or not valid:

Now, we’ll re-run our tests.

$ ginkgo
Running Suite: Adder Suite
==========================
Random Seed: 1553135447
Will run 2 of 2 specs

------------------------------
Failure [0.001 seconds]
Adder
/Users/mark/go/src/github.com/markstgodard/adder/adder_test.go:10
Add
/Users/mark/go/src/github.com/markstgodard/adder/adder_test.go:12
when summand is negative
/Users//go/src/github.com/markstgodard/adder/adder_test.go:24
returns an err [It]
/Users/mark/go/src/github.com/markstgodard/adder/adder_test.go:26
Expected an error to have occurred. Got:
<nil>: nil
/Users/mark/go/src/github.com/markstgodard/adder/adder_test.go:28
------------------------------
Summarizing 1 Failure:[Fail] Adder Add when summand is negative [It] returns an err
/Users/mark/go/src/github.com/markstgodard/adder/adder_test.go:28
Ran 2 of 2 Specs in 0.001 seconds
FAIL! -- 1 Passed | 1 Failed | 0 Pending | 0 Skipped
--- FAIL: TestAdder (0.00s)
FAIL
Ginkgo ran 1 suite in 1.658112523s
Test Suite Failed

Okay, so we have a failing test. Time to update our function to get the test to pass.

Now, we’ll re-run them so that we should have two passing tests.

$ ginkgo
Running Suite: Adder Suite
==========================
Random Seed: 1553135928
Will run 2 of 2 specs
••
Ran 2 of 2 Specs in 0.000 seconds
SUCCESS! -- 2 Passed | 0 Failed | 0 Pending | 0 Skipped
PASS
Ginkgo ran 1 suite in 1.789141688s
Test Suite Passed

Tip: Using -v provides more of a human-readable description of your unit tests.


Summary

Ginkgo is a powerful unit testing framework, providing a BDD-style DSL that runs on top of the standard Go testing infrastructure.

Ginkgo provides the DSL that allows developers to compose tests in a more expressive way.

Describe blocks define the thing you’re testing.

Context blocks define the when of the test (i.e. “when invalid request is received,” “when service is unavailable”).

It blocks run the code to test and state what you expect to happen.

BeforeEach blocks run before each unit test ( It blocks).

AfterEach blocks run after each unit test.

Next steps 🚀

Ginkgo helps you write more expressive unit tests in Go (like the one we did today) to make your program safer and more dependable.

Our next post will take a deeper dive into Ginkgo, including:

  • More advanced assertion matching using Gomega

Boldly Going

Technical insights from the eCommerce experts at Bold Commerce.

Thanks to Ryan Hofer and Bold Commerce

Mark St. Godard

Written by

Director of Software Development at Bold. Previous: IBM and CloudFoundry. I ❤️vim. https://markstgodard.dev

Boldly Going

Technical insights from the eCommerce experts at Bold Commerce.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade