- Covenant:
Goerkin
A Gherkin DSL for Ginkgo
Pronounced just like Gherkin OR Go-erk-in. Your choice.
Inspired by Robbie Clutton's simple_bdd
Goerkin is great for feature tests. Let us know if you use it, and what for! We'd love to hear feedback.
- Designed as an extension to Ginkgo.
- We recommend Gomega matchers.
- For testing web apps, try combining Goerkin with Agouti.
- Use it even if your app isn't written in Go. It's a nice expressive way to build tests for JS, Node and other kinds of apps.
Goals
- Provide the gherkin format for stories
- without a special
*.feature
format
- Local step definitions instead of shared steps which often drives developers toward the wrong abstraction
- Lean on Ginkgo so as not to create a whole other BDD system that needs extensive design and testing
- Promote imperative style tests
- Dissuade the use of BeforeEach/AfterEach
Samples
You can find most of these use cases as actual tests in the documentation test
Simple usage
import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/bunniesandbeatings/goerkin"
)
var _ = Describe("running a total", func() {
var (
total int
)
steps := Define(func(define Definitions) {
define.Given("The current total is cleared", func() {
total = 0
})
define.When("^I add 5$", func() {
total = total + 5
})
define.When("^I add 3$", func() {
total = total + 3
})
define.Then("^The total is 8$", func() {
Expect(total).To(Equal(8))
})
})
Scenario("Adding", func() {
steps.Given("The current total is cleared")
steps.When("I add 5")
steps.And("I add 3")
steps.Then("The total is 8")
})
Scenario("Subtracting with inline definitions", func() {
steps.Given("The current total is cleared")
steps.When("I add 5")
steps.And("I subtract 3", func() {
total = total - 3
})
steps.Then("The total is 2", func() {
Expect(total).To(Equal(2))
})
})
})
Calling steps from within other steps
var _ = Describe("running a total", func() {
var (
total int
steps *Steps
)
steps = Define(func(define Definitions) {
define.Given("The current total is cleared", func() {
total = 0
})
define.When("^I add 5$", func() {
total = total + 5
})
define.When("^I add 3$", func() {
total = total + 3
})
define.When("^I add 5 and 3 to the total$", func() {
steps.Run("I add 5")
steps.Run("I add 3")
})
define.Then("^The total is 8$", func() {
Expect(total).To(Equal(8))
})
})
Scenario("Adding", func() {
steps.Given("The current total is cleared")
steps.When("I add 5 and 3 to the total")
steps.Then("The total is 8")
})
})
Features first
I like my features at the top of the file. You can do that:
var _ = Describe("running a total", func() {
var (
total int
)
steps := NewSteps()
Scenario("Adding", func() {
steps.Given("The current total is cleared")
steps.When("I add 5")
steps.And("I add 3")
steps.Then("The total is 8")
})
Scenario("Subtracting with inline definitions", func() {
steps.Given("The current total is cleared")
steps.When("I add 5")
steps.And("I subtract 3", func() {
total = total - 3
})
steps.Then("The total is 2", func() {
Expect(total).To(Equal(2))
})
})
steps.Define(func(define Definitions) {
define.Given("The current total is cleared", func() {
total = 0
})
define.When("^I add 5$", func() {
total = total + 5
})
define.When("^I add 3$", func() {
total = total + 3
})
define.Then("^The total is 8$", func() {
Expect(total).To(Equal(8))
})
})
})
Cleanup Steps
Givens
and Whens
support cleanup methods
var _ = Describe("Daemonize works", func() {
var (
app *exec.Cmd
)
steps := NewSteps()
Scenario("Running", func() {
steps.Given("My server is running")
steps.When("I visit it's url")
steps.Then("It responds")
})
steps.Define(func(define Definitions) {
define.Given("My server is running",
func() {
app := startMyServer()
},
func() {
// this is a cleanup step
stopMyServer(app)
}
)
... blah, blah blah blablah ...
})
})
Shared Steps
You can define shared steps and re-use them. Within the same
package, package level var's are great for sharing state. Because
we tend to put all our feature tests in one features
package
this has not been an issue. Let us know if you need to share
step definitions across packages, and if you have a solution
you can share.
// your_test.go:
package features_test
var _ = Describe("Shared Steps with the framework", func() {
steps := NewSteps()
Scenario("Use a shared step", func() {
steps.Given("I am a shared step")
steps.Then("I can depend upon it")
})
steps.Define(
sharedSteps, // framework addition
func(define Definitions) {
define.Then(`^I can depend upon it$`, func() {
Expect(sharedValue).To(Equal("shared step called"))
})
},
)
})
// shared_steps_test.go:
package features_test
var sharedValue string
var sharedSteps = func(define Definitions) {
define.Given(`^I am a shared step$`, func() {
sharedValue = "shared step called"
}, func() {
sharedValue = "" // remember to clean up broadly scoped variables
})
}
Unused steps
If you want to find all your unused steps, run the entire suite with env var UNUSED_FAIL
set:
UNUSED_FAIL=true && ginkgo -r .
For shared steps this will fail fast (first Describe block that doesn't use all of the shared steps),
so tread carefully with shared steps. Make sure you run all your tests again with UNUSED_FAIL unset.
Guidelines
Please note that this project is released with a Contributor Code of Conduct.
By participating in this project you agree to abide by its terms.