Skip to content
Spec is a simple BDD-style test organizer for Go that embraces the standard library testing package.
Branch: master
Clone or download
Type Name Latest commit message Commit time
Failed to load latest commit information.
report Hide more spec-internal line numbers from test logs Nov 15, 2018
.gitignore MVP May 14, 2017
.travis.yml Add Go 1.11.x to CI Aug 25, 2018
LICENSE MVP May 14, 2017 Add suite example to README Sep 9, 2018
options_test.go Finish suites feature Sep 9, 2018
parser.go use t.Helper() to mark functions as test helpers Nov 14, 2018
spec.go Hide more spec-internal line numbers from test logs Nov 15, 2018


Build Status GoDoc

Spec is a simple BDD test organizer for Go. It minimally extends the standard library testing package by facilitating easy organization of Go 1.7+ subtests.

Spec differs from other BDD libraries for Go in that it:

  • Does not reimplement or replace any functionality of the testing package
  • Does not provide an alternative test parallelization strategy to the testing package
  • Does not provide assertions
  • Does not encourage the use of dot-imports
  • Does not reuse any closures between test runs (to avoid test pollution)
  • Does not use global state, excessive interface types, or reflection

Spec is intended for gophers who want to write BDD tests in idiomatic Go using the standard library testing package. Spec aims to do "one thing right," and does not provide a wide DSL or any functionality outside of test organization.


  • Clean, simple syntax
  • Supports focusing and pending tests
  • Supports sequential, random, reverse, and parallel test order
  • Provides granular control over test order and subtest nesting
  • Provides a test writer to manage test output
  • Provides a generic, asynchronous reporting interface
  • Provides multiple reporter implementations


  • Use go test -v to see individual subtests.


Most functionality is demonstrated here.

Quick example:

func TestObject(t *testing.T) {
    spec.Run(t, "object", func(t *testing.T, when spec.G, it spec.S) {
        var someObject *myapp.Object

        it.Before(func() {
            someObject = myapp.NewObject()

        it.After(func() {

        it("should have some default", func() {
            if someObject.Default != "value" {
                t.Error("bad default")

        when("something happens", func() {
            it.Before(func() {

            it("should do one thing", func() {
                if err := someObject.DoThing(); err != nil {

            it("should do another thing", func() {
                if result := someObject.DoOtherThing(); result != "good result" {
                    t.Error("bad result")
        }, spec.Random())

        when("some slow things happen", func() {
            it("should do one thing in parallel", func() {
                if result := someObject.DoSlowThing(); result != "good result" {
                    t.Error("bad result")

            it("should do another thing in parallel", func() {
                if result := someObject.DoOtherSlowThing(); result != "good result" {
                    t.Error("bad result")
        }, spec.Parallel())
    }, spec.Report(report.Terminal{}))

With less nesting:

func TestObject(t *testing.T) {
    spec.Run(t, "object", testObject, spec.Report(report.Terminal{}))

func testObject(t *testing.T, when spec.G, it spec.S) {

For focusing/reporting across multiple files in a package:

var suite spec.Suite

func init() {
    suite = spec.New("my suite", spec.Report(report.Terminal{}))
    suite("object", testObject)
    suite("other object", testOtherObject)

func TestObjects(t *testing.T) {

func testObject(t *testing.T, when spec.G, it spec.S) {

func testOtherObject(t *testing.T, when spec.G, it spec.S) {
You can’t perform that action at this time.