gocuke

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Mar 8, 2022 License: Apache-2.0 Imports: 15 Imported by: 0

README

gocuke

Go Reference

gocuke is a Gherkin-based BDD testing library for golang.

Features

  • tight integration with *testing.T (use any standard assertion library or mocking framework)
  • support for passing context between steps using suites which offers better type safety than other generic context approaches
  • auto-discovery of step definitions defined as test suite methods and step definition suggestions for minimal configuration
  • property-based testing via https://github.com/flyingmutant/rapid
  • user-friendly wrapper for data tables
  • support for big integers and big decimals (via https://github.com/cockroachdb/apd)
  • parallel test execution by default
  • full support for all of the latest Gherkin features including rules (via the latest cucumber/gherkin-go and cucumber/messages-go)

Why another golang BDD library?

gocuke is inspired by godog and gobdd. I tried both of these libraries and wanted a specific developer UX that I couldn't achieve with either. godog was not a good fit for the same reasons as that gobdd was created (specifically tight integration with *testing.T). Looking at the source code for gobdd, it wasn't that complex but needed to be updated to a new versions of cucumber/gherkin-go and cucumber/messages-go which was basically a complete rewrite. Happy to coordinate with the authors of either of these libraries at some point to align on common goals.

Quick Start

Step 1: Define some Gherkin

In a file features/simple.feature:

Feature: simple

  Scenario Outline: eat cukes
    Given I have <x> cukes
    When I eat <y>
    Then I have <z> left

    Examples:
      | x | y | z |
      | 5 | 3 | 2 |
      | 10 | 2 | 8 |
Step 2: Setup the test suite

In a file simple_test.go:

package simple

import (
	"github.com/aaronc/gocuke"
	"testing"
)

func TestMinimal(t *testing.T) {
	gocuke.NewRunner(t, func(t gocuke.TestingT) gocuke.Suite {
		return &suite{TestingT: t}
	}).Run()
}

type suite struct {
	gocuke.TestingT
}

When you run the tests, they should fail and suggest that you add these step definitions:

func (s *suite) IEat(a int64) {
    panic("TODO")
}

func (s *suite) IHaveLeft(a int64) {
    panic("TODO")
}

func (s *suite) IHaveCukes(a int64) {
    panic("TODO")
}

Copy these definitions into simple_test.go.

Step 3: Implement Step Definitions

Now implement the step definitions in simple_test.go, adding the variable cukes int64 to suite which tracks state between tests.

NOTE: a new suite is constructed for every test case so it is safe to run tests in parallel, which is the default and what is happening in this example with each of the test cases in the Scenario Outline.

type suite struct {
	gocuke.TestingT
	cukes int64
}

func (s *suite) IHaveCukes(a int64) {
	s.cukes = a
}

func (s *suite) IEat(a int64) {
	s.cukes -= a
}

func (s *suite) IHaveLeft(a int64) {
	if a != s.cukes {
		s.Fatalf("expected %d cukes, have %d", a, s.cukes)
	}
}

Your tests should now pass!

Usage Details

Custom options

Runner has the following methods for setting custom options

  • WithPath() sets custom paths. The default is features/*.feature.
  • Step() can be used to add custom steps with special regular expressions. The first argument of custom step definitions must still be the suite.
  • NonParallel() disables parallel tests.
Supported Param Types

gocuke supports the following parameter types:

  • string
  • int64
  • *big.Int
  • *apd.Decimal

float64 support is not planned because it is lossy!!

Doc Strings and Data Tables

gocuke.DocString or gocuke.DataTable should be used as the last argument in a step definition if the step uses a doc string or data table.

Cleanup Methods

Test suites that define a Cleanup method will have that method called at the end of testing (regardless of whether the test was successful).

Property-based testing using Rapid

Property-based tests using https://github.com/flyingmutant/rapid can be enabled by using *rapid.T as the first argument of test methods (after the suite receiver argument). Property-based test cases will be run as many times is rapid is configured to run tests.

Example:

Scenario: any int64 value
  Given any int64 string
  When when I convert it to an int64
  Then I get back the original value
type suite struct {
  TestingT
  
  x, parsed   int64
  str    string
}

func (s *valuesSuite) AnyInt64String(t *rapid.T) {
	s.x = rapid.Int64().Draw(t, "x").(int64)
	s.str = fmt.Sprintf("%d", s.x)
}

func (s *valuesSuite) WhenIConvertItToAnInt64() {
  s.parsed = toInt64(s, s.str)
}


func (s *suite) IGetBackTheOriginalValue() {
  assert.Equal(s, s.x, s.parsed)
}

Roadmap

  • tag expression support
  • Cucumber message based reporting

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Cell

type Cell struct {
	// contains filtered or unexported fields
}

Cell represents a data table cell.

func (Cell) BigInt

func (c Cell) BigInt() *big.Int

BigInt returns the cell as a *big.Int.

func (Cell) Decimal

func (c Cell) Decimal() *apd.Decimal

Decimal returns the cell value as an *apd.Decimal.

func (Cell) Int64

func (c Cell) Int64() int64

Int64 returns the cell as an int64.

func (Cell) String

func (c Cell) String() string

String returns the cell value as a string.

type DataTable

type DataTable struct {
	// contains filtered or unexported fields
}

DataTable wraps a data table step argument

func (DataTable) Cell

func (d DataTable) Cell(row, col int) *Cell

Cell returns the cell at the provided 0-based row and col offset.

func (DataTable) HeaderTable

func (d DataTable) HeaderTable() *HeaderTable

HeaderTable returns the data table as a header table which is a wrapper around the table which assumes that the first row is the table header.

func (DataTable) NumCols

func (d DataTable) NumCols() int

NumCols returns the number of columns in the data table.

func (DataTable) NumRows

func (d DataTable) NumRows() int

NumRows returns the number of rows in the data table.

type DocString

type DocString struct {
	MediaType string
	Content   string
}

DocString represents a doc string step argument.

type HeaderTable

type HeaderTable struct {
	DataTable
	// contains filtered or unexported fields
}

HeaderTable is a wrapper around a table which assumes that the first row is \ the table header.

func (*HeaderTable) Get

func (h *HeaderTable) Get(row int, col string) *Cell

Get returns the cell at the provided row offset (skipping the header row) and column name (as indicated in the header).

func (*HeaderTable) NumRows

func (h *HeaderTable) NumRows() int

NumRows returns the number of rows in the table (excluding the header row).

type Runner

type Runner struct {
	// contains filtered or unexported fields
}

Runner is a test runner.

func NewRunner

func NewRunner(t *testing.T, initScenario func(t TestingT) Suite) *Runner

NewRunner constructs a new Runner with the provided initScenario function. initScenario will be called for each test case returning a new suite instance for each test case which can be used for sharing state between steps. It is expected that the suite will retain a copy of the TestingT instance for usage in each step.

func (*Runner) NonParallel

func (r *Runner) NonParallel() *Runner

NonParallel instructs the runner not to run tests in parallel (which is the default).

func (*Runner) Run

func (r *Runner) Run()

Run runs the features registered with the runner.

func (*Runner) Step

func (r *Runner) Step(step interface{}, definition interface{}) *Runner

Step can be used to manually register a step with the runner. step should be a string or *regexp.Regexp instance. definition should be a function which takes the suite as its first argument (usually an instance method), parameter arguments next (with string, int64, *big.Int, and *apd.Decimal as valid parameter values) and gocuke.DocString or gocuke.DataTable as the last argument if this step uses a doc string or data table respectively. Custom step definitions will always take priority of auto-discovered step definitions.

func (*Runner) WithPath

func (r *Runner) WithPath(paths ...string) *Runner

WithPath specifies glob paths for the runner to look up .feature files. The default is `features/*.feature`.

type Suite

type Suite interface{}

type TestingT

type TestingT interface {
	Error(args ...any)
	Errorf(format string, args ...any)
	Fail()
	FailNow()
	Failed() bool
	Fatal(args ...any)
	Fatalf(format string, args ...any)
	Log(args ...any)
	Logf(format string, args ...any)
	Skip(args ...any)
	SkipNow()
	Skipf(format string, args ...any)
	Helper()
}

TestingT is the common subset of testing methods exposed to test suite instances and expected by common assertion and mocking libraries.

Jump to

Keyboard shortcuts

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