Go and Test: GoConvey

BDD with GoConvey

GoConvey is a very good suite to test in Go. It has a lot of interesting characteristics, it is very flexible and it help us do testing. You could/can see the project in this web site http://goconvey.co/

In this post I will explain how I use GoConvey with Go to do BDD.

GoConvey doesn’t use feature file, and it’s not strict about gherkin syntax, but you can use this package to do BDD. If your steps are the same in several features, you must do something to share the code, it’s not automatic like cucumber implementations. This could make you have more duplicated code and your features will be more disordered than with cucumber. But in exchange of this you will have a better integration with go testing and less problems with coverages reports.

The project repository and Readme reference here

And Don’t pass without looking her wiki

install

Import the project in your GOPATH directory

 go get github.com/smartystreets/goconvey

Then import in you poject

import ( “testing” . “github.com/smartystreets/goconvey/convey” )

How use GoConvey to BDD

I have a template Scenary ready to copy in you new feature test.

func TestTemplate(t *testing.T) {

	Convey("Scenario: ", t, func() {
		Convey("Given ", func() {
			Convey("When ", func() {
				Convey("Then ", func() {

				})
			})
		})
	})
}

This way you could have a BDD test. I usually put this in a feature_test.go file that is the feature that I want to implement.

But we have another alternatives to join scenarios in one feature.

First let’s take a look at output of go test -v execution

=== RUN   TestTemplate

  Scenario: 
    Given 
      When 
        Then 


0 total assertions

--- PASS: TestTemplate (0.00s)
PASS
ok      command-line-arguments  0.001s

Grouping in Feature

If you want grouping scenarios in feature to get gruping output:

=== RUN   TestFeatures

  Feature: It is a example feature with several scenarios 
    Scenario: one 
      Given 
        When 
          Then 
    Scenario: two 
      Given 
        When 
          Then 

Then you could something like that:

func TestFeatures(t *testing.T) {
	Convey(`Feature: It is a example feature with several scenarios`, t, func() {
		scenarioOne(t)
		scenarioTwo(t)
	})
}

func scenarioOne(t *testing.T) {
	Convey("Scenario: one", func() {
		Convey("Given ", func() {
			Convey("When ", func() {
				Convey("Then ", func() {
				})
			})
		})
	})
}

func scenarioTwo(t *testing.T) {
	Convey("Scenario: two ", func() {
		Convey("Given ", func() {
			Convey("When ", func() {
				Convey("Then ", func() {
				})
			})
		})
	})
}

Or you could have a severla scenarios in same or differents files of test

func TestOne(t *testing.T) {
	Convey("Scenario: one ", t, func() {
		Convey("Given ", func() {
			Convey("When ", func() {
				Convey("Then ", func() {
				})
			})
		})
	})
}

func TestTwo(t *testing.T) {
	Convey("Scenario: two ", t, func() {
		Convey("Given ", func() {
			Convey("When ", func() {
				Convey("Then ", func() {
				})
			})
		})
	})
}

=== RUN   TestOne

  Scenario: one
    Given 
      When 
        Then 


0 total assertions

--- PASS: TestOne (0.00s)
=== RUN   TestTwo

  Scenario: two 
    Given 
      When 
        Then 


0 total assertions

--- PASS: TestTwo (0.00s)

Assertions

The assertions with GoConvery are using with method So(). There are many of then and you could implement your own assertions.

Look the wiki/Assertions to see all availables, and here to see a custom example, is really easy added one, you can added a function to assert all type of complex situations.

=== RUN   TestTemplate

  Scenario: 
    Given a integer number ✔
      When divided by 2 
        Then the rest must be 0 ✘


Failures:

  * /home/fsolana/go/src/grutestgo/service_template_test.go 
  Line 19:
  Expected: '0'
  Actual:   '1'
  (Should be equal)
  goroutine 10 [running]:
  grutestgo.TestTemplate.func1.1.1.1()
  	/home/fsolana/go/src/grutestgo/service_template_test.go:19 +0xa1
  github.com/jtolds/gls.(*ContextManager).SetValues.func1(0x0)
func TestTemplate(t *testing.T) {

	Convey("Scenario: ", t, func() {
		Convey("Given a integer number ", func() {
			myNumber := 1
			So(myNumber, ShouldHaveSameTypeAs, 0)
			Convey("When divided by 2 ", func() {
				rest := CalculateRest(myNumber)
				Convey("Then the rest must be 0", func() {
					So(rest, ShouldEqual, 0)
				})
			})
		})
	})
}

You can do any type of assertion that you need is very easy to do, only you have return empty string when all is ok, and description text when error happen.

func TestTemplate(t *testing.T) {

	Convey("Scenario: ", t, func() {
		Convey("Given a integer number ", func() {
			myNumber := 2
			So(myNumber, ShouldHaveSameTypeAs, 0)
			Convey("When divided by 2 ", func() {
				rest := CalculateRest(myNumber)
				Convey("Then the rest must be 0", func() {
					So(rest, shoudBeThatIWantBecaouseIsMyCustomAssertion, myNumber, "blue")
				})
			})
		})
	})
}

func shoudBeThatIWantBecaouseIsMyCustomAssertion(actual interface{}, expected ...interface{}) string {
	colourNumber := make(map[int]string)
	colourNumber[2] = "red"
	number := expected[0].(int)
	colour := expected[1].(string)
	if actual.(int) != 0 {
		return "The result wasn't 0"
	}
	if colourNumber[number] != colour {
		return "The result number is not appropriated colour"
	}

	return ""
}

Conclusions

Convey is a powerful tool to did BDD and TDD. You can’t begin with feature file but allow you develop quickly your test and have a the assertions are awsome

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.