Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

  • Blog Navigation
Fake it and Go!

BDD-ing in Go follows the same basic drill of writing a top-level behaviour driven acceptance test to drive out the feature and then lots of unit tests for individual structs and methods. But its implementation is tricky initially because Go does not really have the idea of a mocking library. Coming from a Rails/Javascript background writing unit tests without mocks/stubs/spies requires you to think about them a little differently. We’ve been doing it quite successfully using dependency injection and lightweight ‘fakes’ that conform to the same interface as their production counterparts.

Writing your own mock/stub:

Let’s write a simple network checker that determines whether you can connect to the internet based on your ability to ping some server. These tests use a FakePing, which conforms to the interface Ping but gives the ability to modify its behaviour:

It("should return a success message if ping succeeds", func() {
	fakePing := FakePing{}
	networkChecker := NetworkChecker{Ping: fakePing}

	Expect(networkChecker.check()).To(Equal("You are on the internet!"))

It("should return an error message if ping fails", func() {
	fakePing := FakePing{Error: errors.New("Cannot reach server")}
	networkChecker := NetworkChecker{Ping: fakePing}

	Expect(networkChecker.check()).To(Equal("Oh noes! You can't reach the internet."))

A fake typically:

  • lives in a separate package
  • has a few extra attributes that allow the tests to modify their behaviour
  • has very simplistic methods
package fakes

type FakePing struct{
	Error error

func (ping FakePing) Status() bool {
	if ping.Error != nil {
		return false

	return true
type Ping interface{
	Status() bool

type NetworkChecker struct{
	Ping Ping

func (nc *NetworkChecker) check() string {
	if nc.Ping.Status() == true {
		return "You are on the internet!"
	} else {
		return "Oh noes! You can't reach the internet."

Writing a spy:

Here’s a simple Terminal that only knows to echo “the good stuff”. This test uses FakeEcho as a spy. FakeEcho has a short Say() which captures the arguments it was called with, which enables you to assert that you called the method with the right arguments:

It("should echo the good stuff", func() {
	fakeEcho := &FakeEcho{}
	terminal := Terminal{Echo: fakeEcho}

	Expect(fakeEcho.Text).To(Equal("the good stuff"))
package fakes

type FakeEcho struct {
	Text string

func (echo *FakeEcho) Say(text string) {
	echo.Text = text
type Echo interface{
	Say(text string)

type Terminal struct{
	Echo Echo

func(t *Terminal) doTheGoodStuff() {
	t.Echo.Say("the good stuff")

Notice that so far even though you're green, you don't have an actual Ping{} or Echo{}. Make sure you start with a top-level acceptance test that drives your feature and ensures that you have all the wiring in place. You may choose to remove it before the end of story, when you have confidence in your unit tests, but its a great starting point and ensures you've got all the moving pieces in place.

It("should return a success message if it can get to the internet", func() {
	serverStatus := HealthCheck{}

	Expect(serverStatus.checkInternet()).To(Equal("You are on the internet"))
type ping struct {}

func (p *ping) Status() bool {
	cmd := exec.Command("ping", "")
	_, err := cmd.CombinedOutput()

	if err == nil {
		return true

	return false
type HealthCheck struct {}

func (hc *HealthCheck) checkInternet() string {
	ping := ping{}
	networkCheck := NetworkChecker{Ping: &ping}

	return networkCheck.check()
  1. Santiago says:

    Excellent post! What bdd framework is used for GO?

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *

Share This