goerkin

package module
v2.0.0-...-55ed25a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 15, 2023 License: Apache-2.0 Imports: 6 Imported by: 0

README

  • Covenant: Contributor 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.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var FScenario = ginkgo.FIt
View Source
var Scenario = ginkgo.It
View Source
var XScenario = ginkgo.XIt

Functions

This section is empty.

Types

type Definitions

type Definitions interface {
	Given(re string, given interface{}, after ...func())
	When(re string, when interface{}, after ...func())
	Then(re string, then interface{}, after ...func())
}

type Steps

type Steps struct {
	Fail func(message string, callerSkip ...int)
	// contains filtered or unexported fields
}

func Define

func Define(body ...interface{}) *Steps

func NewSteps

func NewSteps() *Steps

func (*Steps) And

func (s *Steps) And(text string, body ...bodyFn)

func (*Steps) Define

func (s *Steps) Define(bodies ...defineBodyFn)

func (*Steps) Given

func (s *Steps) Given(text string, body ...bodyFn)

func (*Steps) Run

func (s *Steps) Run(text string)

func (*Steps) Then

func (s *Steps) Then(text string, body ...bodyFn)

func (*Steps) UnusedSteps

func (s *Steps) UnusedSteps() []string

func (*Steps) When

func (s *Steps) When(text string, body ...bodyFn)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL