Skip to content

Commit

Permalink
Catching invalid step functions (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
Bartłomiej Klimczak committed Feb 3, 2020
1 parent 94c7657 commit f847eef
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 91 deletions.
54 changes: 36 additions & 18 deletions gobdd.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ import (

// Holds all the information about the suite (options, steps to execute etc)
type Suite struct {
t *testing.T
steps []stepDef
options SuiteOptions
t TestingT
steps []stepDef
options SuiteOptions
stepsErrors []error
}

// Holds all the information about how the suite or features/steps should be configured
Expand Down Expand Up @@ -91,8 +92,17 @@ type stepDef struct {
f interface{}
}

type TestingT interface {
Log(...interface{})
Fatal(...interface{})
Fatalf(string, ...interface{})
Parallel()
Fail()
Run(name string, f func(t *testing.T)) bool
}

// Creates a new suites with given configuration and empty steps defined
func NewSuite(t *testing.T, options SuiteOptions) *Suite {
func NewSuite(t TestingT, options SuiteOptions) *Suite {
return &Suite{
t: t,
steps: []stepDef{},
Expand All @@ -112,18 +122,23 @@ func NewSuite(t *testing.T, options SuiteOptions) *Suite {
// func myStepFunction(ctx context.Context, first int, second int) (context.Context, error) {
// return ctx, nil
// }
func (s *Suite) AddStep(expr string, step interface{}) error {
func (s *Suite) AddStep(expr string, step interface{}) {
err := validateStepFunc(step)
if err != nil {
return err
s.stepsErrors = append(s.stepsErrors, err)
return
}

compiled, err := regexp.Compile(expr)
if err != nil {
s.stepsErrors = append(s.stepsErrors, err)
return
}

s.steps = append(s.steps, stepDef{
expr: regexp.MustCompile(expr),
expr: compiled,
f: step,
})

return nil
}

// AddRegexStep registers a step in the suite.
Expand All @@ -138,22 +153,29 @@ func (s *Suite) AddStep(expr string, step interface{}) error {
// func myStepFunction(ctx context.Context, first int, second int) (context.Context, error) {
// return ctx, nil
// }
func (s *Suite) AddRegexStep(expr *regexp.Regexp, step interface{}) error {
func (s *Suite) AddRegexStep(expr *regexp.Regexp, step interface{}) {
err := validateStepFunc(step)
if err != nil {
return err
s.stepsErrors = append(s.stepsErrors, err)
return
}

s.steps = append(s.steps, stepDef{
expr: expr,
f: step,
})

return nil
}

// Executes the suite with given options and defined steps
func (s *Suite) Run() {
if len(s.stepsErrors) > 0 {
for _, err := range s.stepsErrors {
s.t.Log(err)
}
s.t.Fatal("the test contains invalid step definitions")
return
}

files, err := filepath.Glob(s.options.featuresPaths)
if err != nil {
s.t.Fatalf("cannot find features/ directory")
Expand Down Expand Up @@ -260,11 +282,7 @@ func (s *Suite) stepsFromExample(sourceStep *messages.GherkinDocument_Feature_St
}

expr := strings.Replace(def.expr.String(), ph, t, -1)
err = s.AddStep(expr, def.f)

if err != nil {
continue
}
s.AddStep(expr, def.f)
}
}

Expand Down
98 changes: 44 additions & 54 deletions gobdd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,81 +4,48 @@ import (
"context"
"errors"
"testing"

"github.com/go-bdd/assert"
)

func TestScenarios(t *testing.T) {
suite := NewSuite(t, NewSuiteOptions().WithFeaturesPath("features/example.feature"))
err := suite.AddStep(`I add (\d+) and (\d+)`, add)
if err != nil {
t.Fatal(err)
}

err = suite.AddStep(`the result should equal (\d+)`, check)
if err != nil {
t.Fatal(err)
}
suite.AddStep(`I add (\d+) and (\d+)`, add)
suite.AddStep(`the result should equal (\d+)`, check)

suite.Run()
}

func TestDifferentFuncTypes(t *testing.T) {
suite := NewSuite(t, NewSuiteOptions().WithFeaturesPath("features/func_types.feature"))
err := suite.AddStep(`I add ([+-]?[0-9]*[.]?[0-9]+) and ([+-]?[0-9]*[.]?[0-9]+)`, addf)
if err != nil {
t.Fatal(err)
}

err = suite.AddStep(`the result should equal ([+-]?[0-9]*[.]?[0-9]+)`, checkf)
if err != nil {
t.Fatal(err)
}
suite.AddStep(`I add ([+-]?[0-9]*[.]?[0-9]+) and ([+-]?[0-9]*[.]?[0-9]+)`, addf)
suite.AddStep(`the result should equal ([+-]?[0-9]*[.]?[0-9]+)`, checkf)

suite.Run()
}

func TestScenarioOutline(t *testing.T) {
suite := NewSuite(t, NewSuiteOptions().WithFeaturesPath("features/outline.feature"))
err := suite.AddStep(`I add <digit1> and <digit2>`, add)
if err != nil {
t.Fatal(err)
}

err = suite.AddStep(`the result should equal <result>`, check)
if err != nil {
t.Fatal(err)
}
suite.AddStep(`I add (\d+) and (\d+)`, add)
suite.AddStep(`the result should equal <result>`, check)

suite.Run()
}

func TestBackground(t *testing.T) {
options := NewSuiteOptions().WithFeaturesPath("features/background.feature")
suite := NewSuite(t, options)
err := suite.AddStep(`I add (\d+) and (\d+)`, add)
if err != nil {
t.Fatal(err)
}

err = suite.AddStep(`the result should equal (\d+)`, check)
if err != nil {
t.Fatal(err)
}
suite.AddStep(`I add (\d+) and (\d+)`, add)
suite.AddStep(`the result should equal (\d+)`, check)

suite.Run()
}

func TestTags(t *testing.T) {
options := NewSuiteOptions().WithFeaturesPath("features/tags.feature").WithTags([]string{"@tag"})
suite := NewSuite(t, options)
err := suite.AddStep(`fail the test`, fail)
if err != nil {
t.Fatal(err)
}

err = suite.AddStep(`the test should pass`, pass)
if err != nil {
t.Fatal(err)
}
suite.AddStep(`fail the test`, fail)
suite.AddStep(`the test should pass`, pass)

suite.Run()
}
Expand All @@ -87,11 +54,7 @@ func TestIgnoredTags(t *testing.T) {
options := NewSuiteOptions().WithFeaturesPath("features/ignored_tags.feature")
options = options.WithIgnoredTags([]string{"@ignore"})
suite := NewSuite(t, options)
err := suite.AddStep(`fail the test`, fail)
if err != nil {
t.Fatal(err)
}

suite.AddStep(`fail the test`, fail)
suite.Run()
}

Expand All @@ -108,10 +71,13 @@ func TestInvalidFunctionSignature(t *testing.T) {

for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
suite := NewSuite(t, NewSuiteOptions())
err := suite.AddStep("", testCase.f)
if err == nil {
t.Errorf("the function has invalid signature; the test should fail")
tester := &mockTester{}
suite := NewSuite(tester, NewSuiteOptions())
suite.AddStep("", testCase.f)
suite.Run()
err := assert.Equals(1, tester.fatalCalled)
if err != nil {
t.Fatal(err)
}
})
}
Expand Down Expand Up @@ -156,3 +122,27 @@ func fail(ctx context.Context) (context.Context, error) {
func pass(ctx context.Context) (context.Context, error) {
return ctx, nil
}

type mockTester struct {
fatalCalled int
}

func (m mockTester) Log(...interface{}) {
}

func (m *mockTester) Fatal(...interface{}) {
m.fatalCalled++
}

func (m mockTester) Fatalf(string, ...interface{}) {
}

func (m mockTester) Parallel() {
}

func (m mockTester) Fail() {
}

func (m mockTester) Run(name string, f func(t *testing.T)) bool {
return true
}
25 changes: 13 additions & 12 deletions testhttp/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/go-bdd/assert"
"io/ioutil"
"net/http"

"github.com/go-bdd/assert"
)

type addStepper interface {
AddStep(definition string, step interface{}) error
AddStep(definition string, step interface{})
}

type testHTTPMethods struct {
Expand All @@ -29,16 +30,16 @@ func Build(addStep addStepper, h httpHandler) TestHTTP {

testHTTP := testHTTPMethods{tHTTP: thhtp}

_ = addStep.AddStep(`^I make a (GET|POST|PUT|DELETE|OPTIONS) request to "([^"]*)"$`, testHTTP.makeRequest)
_ = addStep.AddStep(`^the response code equals (\d+)$`, testHTTP.statusCodeEquals)
_ = addStep.AddStep(`^the response contains a valid JSON$`, testHTTP.validJSON)
_ = addStep.AddStep(`^the response is "(.*)"$`, testHTTP.theResponseIs)
_ = addStep.AddStep(`^the response header "(.*)" equals "(.*)"$`, testHTTP.responseHeaderEquals)
_ = addStep.AddStep(`^I have a (GET|POST|PUT|DELETE|OPTIONS) request "(.*)"$`, testHTTP.iHaveARequest)
_ = addStep.AddStep(`^I set request header "(.*)" to "(.*)"$`, testHTTP.iSetRequestSetTo)
_ = addStep.AddStep(`^I set request body to "([^"]*)"$`, testHTTP.iSetRequestBodyTo)
_ = addStep.AddStep(`^the request has body "(.*)"$`, testHTTP.theRequestHasBody)
_ = addStep.AddStep(`^I make the request$`, testHTTP.iMakeRequest)
addStep.AddStep(`^I make a (GET|POST|PUT|DELETE|OPTIONS) request to "([^"]*)"$`, testHTTP.makeRequest)
addStep.AddStep(`^the response code equals (\d+)$`, testHTTP.statusCodeEquals)
addStep.AddStep(`^the response contains a valid JSON$`, testHTTP.validJSON)
addStep.AddStep(`^the response is "(.*)"$`, testHTTP.theResponseIs)
addStep.AddStep(`^the response header "(.*)" equals "(.*)"$`, testHTTP.responseHeaderEquals)
addStep.AddStep(`^I have a (GET|POST|PUT|DELETE|OPTIONS) request "(.*)"$`, testHTTP.iHaveARequest)
addStep.AddStep(`^I set request header "(.*)" to "(.*)"$`, testHTTP.iSetRequestSetTo)
addStep.AddStep(`^I set request body to "([^"]*)"$`, testHTTP.iSetRequestBodyTo)
addStep.AddStep(`^the request has body "(.*)"$`, testHTTP.theRequestHasBody)
addStep.AddStep(`^I make the request$`, testHTTP.iMakeRequest)

return thhtp
}
Expand Down
15 changes: 8 additions & 7 deletions testhttp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package gobdd

import (
"context"
"github.com/go-bdd/assert"
"net/http"
"testing"

"github.com/go-bdd/assert"

"github.com/go-bdd/gobdd/testhttp"
)

Expand All @@ -30,11 +31,11 @@ func TestHTTP(t *testing.T) {
w.WriteHeader(http.StatusOK)
})

_ = s.AddStep(`^the request has method set to (GET|POST|PUT|DELETE|OPTIONS)$`, requestHasMethodSetTo)
_ = s.AddStep(`^the url is set to "([^"]*)"$`, urlIsSetTo)
_ = s.AddStep(`^the request body is nil$`, requestBodyIsNil)
_ = s.AddStep(`^I set the header "([^"]*)" to "([^"]*)"$`, ISetHeaderTo)
_ = s.AddStep(`^the request has header "([^"]*)" set to "([^"]*)"$`, requestHasHeaderSetTo)
s.AddStep(`^the request has method set to (GET|POST|PUT|DELETE|OPTIONS)$`, requestHasMethodSetTo)
s.AddStep(`^the url is set to "([^"]*)"$`, urlIsSetTo)
s.AddStep(`^the request body is nil$`, requestBodyIsNil)
s.AddStep(`^I set the header "([^"]*)" to "([^"]*)"$`, ISetHeaderTo)
s.AddStep(`^the request has header "([^"]*)" set to "([^"]*)"$`, requestHasHeaderSetTo)
testhttp.Build(s, router)

s.Run()
Expand Down Expand Up @@ -62,7 +63,7 @@ func ISetHeaderTo(ctx context.Context, headerName, value string) (context.Contex
return ctx, nil
}

func requestHasHeaderSetTo(ctx context.Context, headerName,expected string) (context.Context, error) {
func requestHasHeaderSetTo(ctx context.Context, headerName, expected string) (context.Context, error) {
r := ctx.Value(testhttp.RequestKey{}).(*http.Request)
given := r.Header.Get(headerName)
return ctx, assert.Equals(expected, given)
Expand Down

0 comments on commit f847eef

Please sign in to comment.