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