diff --git a/README.md b/README.md index 61529de..6d3bfbc 100644 --- a/README.md +++ b/README.md @@ -77,8 +77,7 @@ themselves from costly regressions. ## Example -The following example can be [found -here](/_examples/godogs). +The following example can be [found here](/_examples/godogs). ### Step 1 @@ -345,6 +344,40 @@ is one of the following: - `@wip && ~@new` - run wip scenarios, but exclude new - `@wip,@undone` - run wip or undone scenarios +### Using assertion packages like testify with Godog +A more extensive example can be [found here](/_examples/assert-godogs). + +``` go +/* file: $GOPATH/src/assert-godogs/godogs_test.go */ + +func thereShouldBeRemaining(remaining int) error { + return assertExpectedAndActual( + assert.Equal, Godogs, remaining, + "Expected %d godogs to be remaining, but there is %d", remaining, Godogs, + ) +} + +// assertExpectedAndActual is a helper function to allow the step function to call +// assertion functions where you want to compare an expected and an actual value. +func assertExpectedAndActual(a expectedAndActualAssertion, expected, actual interface{}, msgAndArgs ...interface{}) error { + var t asserter + a(&t, expected, actual, msgAndArgs...) + return t.err +} + +type expectedAndActualAssertion func(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool + +// asserter is used to be able to retrieve the error reported by the called assertion +type asserter struct { + err error +} + +// Errorf is used by the called assertion to report an error +func (a *asserter) Errorf(format string, args ...interface{}) { + a.err = fmt.Errorf(format, args...) +} +``` + ### Configure common options for godog CLI There are no global options or configuration files. Alias your common or diff --git a/_examples/assert-godogs/features/godogs.feature b/_examples/assert-godogs/features/godogs.feature new file mode 100644 index 0000000..d333d55 --- /dev/null +++ b/_examples/assert-godogs/features/godogs.feature @@ -0,0 +1,15 @@ +# file: $GOPATH/godogs/features/godogs.feature +Feature: eat godogs + In order to be happy + As a hungry gopher + I need to be able to eat godogs + + Scenario: Eat 5 out of 12 + Given there are 12 godogs + When I eat 4 + Then there should be 7 remaining + + Scenario: Eat 12 out of 12 + Given there are 12 godogs + When I eat 11 + Then there should be none remaining diff --git a/_examples/assert-godogs/godogs.go b/_examples/assert-godogs/godogs.go new file mode 100644 index 0000000..b735a87 --- /dev/null +++ b/_examples/assert-godogs/godogs.go @@ -0,0 +1,7 @@ +/* file: $GOPATH/src/godogs/godogs.go */ +package main + +// Godogs available to eat +var Godogs int + +func main() { /* usual main func */ } diff --git a/_examples/assert-godogs/godogs_test.go b/_examples/assert-godogs/godogs_test.go new file mode 100644 index 0000000..b59ba5c --- /dev/null +++ b/_examples/assert-godogs/godogs_test.go @@ -0,0 +1,108 @@ +/* file: $GOPATH/src/assert-godogs/godogs_test.go */ +package main + +import ( + "flag" + "fmt" + "os" + "testing" + + "github.com/cucumber/godog" + "github.com/cucumber/godog/colors" + messages "github.com/cucumber/messages-go/v10" + "github.com/stretchr/testify/assert" +) + +var opt = godog.Options{Output: colors.Colored(os.Stdout)} + +func init() { + godog.BindFlags("godog.", flag.CommandLine, &opt) +} + +func TestMain(m *testing.M) { + flag.Parse() + opt.Paths = flag.Args() + + status := godog.RunWithOptions("godogs", func(s *godog.Suite) { + FeatureContext(s) + }, opt) + + if st := m.Run(); st > status { + status = st + } + os.Exit(status) +} + +func thereAreGodogs(available int) error { + Godogs = available + return nil +} + +func iEat(num int) error { + err := assertExpectedAndActual( + assert.GreaterOrEqual, Godogs, num, + "You cannot eat %d godogs, there are %d available", num, Godogs, + ) + if err != nil { + return err + } + + Godogs -= num + return nil +} + +func thereShouldBeRemaining(remaining int) error { + return assertExpectedAndActual( + assert.Equal, Godogs, remaining, + "Expected %d godogs to be remaining, but there is %d", remaining, Godogs, + ) +} + +func thereShouldBeNoneRemaining() error { + return assertActual( + assert.Empty, Godogs, + "Expected none godogs to be remaining, but there is %d", Godogs, + ) +} + +func FeatureContext(s *godog.Suite) { + s.Step(`^there are (\d+) godogs$`, thereAreGodogs) + s.Step(`^I eat (\d+)$`, iEat) + s.Step(`^there should be (\d+) remaining$`, thereShouldBeRemaining) + s.Step(`^there should be none remaining$`, thereShouldBeNoneRemaining) + + s.BeforeScenario(func(*messages.Pickle) { + Godogs = 0 // clean the state before every scenario + }) +} + +// assertExpectedAndActual is a helper function to allow the step function to call +// assertion functions where you want to compare an expected and an actual value. +func assertExpectedAndActual(a expectedAndActualAssertion, expected, actual interface{}, msgAndArgs ...interface{}) error { + var t asserter + a(&t, expected, actual, msgAndArgs...) + return t.err +} + +type expectedAndActualAssertion func(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool + +// assertActual is a helper function to allow the step function to call +// assertion functions where you want to compare an actual value to a +// predined state like nil, empty or true/false. +func assertActual(a actualAssertion, actual interface{}, msgAndArgs ...interface{}) error { + var t asserter + a(&t, actual, msgAndArgs...) + return t.err +} + +type actualAssertion func(t assert.TestingT, actual interface{}, msgAndArgs ...interface{}) bool + +// asserter is used to be able to retrieve the error reported by the called assertion +type asserter struct { + err error +} + +// Errorf is used by the called assertion to report an error +func (a *asserter) Errorf(format string, args ...interface{}) { + a.err = fmt.Errorf(format, args...) +}