Repository: go-bdd/gobdd
Pull request #70 (2020-02-15 21:10:06)

Use functional options
This will solve this [issue](https://github.com/go-bdd/gobdd/issues/49). Each of the options method will return a closure that changes the form of an options variable. Then in NewSuite function it start with default options and is passed to all closures in left to right order. Also changed the tests to work with the new API and all the tests passed!

I also added a minor modification runStep function, changed the format from %s %s to %s%s , as the former create unnecessary space.

bkielbasa: Awesome job!
Just one thing: recently, I put [docs](https://github.com/go-bdd/gobdd/tree/master/docs) to the main repo so please update the docs as well to keep it consistent.
arberiii: Thanks 😃 . I only found one place where the new format is inconsistent and changed the example in docs. 
bkielbasa: Awesome! Thank you :) 

Patch:
diff --git a/go.mod b/go.mod
index 6a12ba7..ba3bc61 100644
--- a/go.mod
+++ b/go.mod
@@ -6,5 +6,5 @@ require (
 	github.com/cucumber/gherkin-go/v9 v9.2.0
 	github.com/cucumber/messages-go/v9 v9.0.3
 	github.com/go-bdd/assert v0.0.0-20190820124234-20d47a68475d
-	github.com/logrusorgru/aurora v0.0.0-20190428105938-cea283e61946
+	github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381
 )
diff --git a/go.sum b/go.sum
index bc93978..4f8e389 100644
--- a/go.sum
+++ b/go.sum
@@ -22,6 +22,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/logrusorgru/aurora v0.0.0-20190428105938-cea283e61946 h1:z+WaKrgu3kCpcdnbK9YG+JThpOCd1nU5jO5ToVmSlR4=
 github.com/logrusorgru/aurora v0.0.0-20190428105938-cea283e61946/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
+github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs=
+github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
diff --git a/gobdd.go b/gobdd.go
index aef66d3..2f57ae2 100644
--- a/gobdd.go
+++ b/gobdd.go
@@ -48,43 +48,49 @@ func NewSuiteOptions() SuiteOptions {
 	}
 }
 
-// runs tests in parallel
-func (options SuiteOptions) RunInParallel() SuiteOptions {
-	options.runInParallel = true
-	return options
+// RunInParallel runs tests in parallel
+func RunInParallel() func(*SuiteOptions) {
+	return func(options *SuiteOptions) {
+		options.runInParallel = true
+	}
 }
 
-// Configures a pattern (regexp) where feature can be found.
+// WithFeaturesPath configures a pattern (regexp) where feature can be found.
 // The default value is "features/*.feature"
-func (options SuiteOptions) WithFeaturesPath(path string) SuiteOptions {
-	options.featuresPaths = path
-	return options
+func WithFeaturesPath(path string) func(*SuiteOptions) {
+	return func(options *SuiteOptions) {
+		options.featuresPaths = path
+	}
 }
 
-// Configures which tags should be skipped while executing a suite
+// WithTags configures which tags should be skipped while executing a suite
 // Every tag has to start with @
-func (options SuiteOptions) WithTags(tags []string) SuiteOptions {
-	options.tags = tags
-	return options
+func WithTags(tags []string) func(*SuiteOptions) {
+	return func(options *SuiteOptions) {
+		options.tags = tags
+	}
 }
 
-// Configures functions that should be executed before every scenario
-func (options SuiteOptions) WithBeforeScenario(f func()) SuiteOptions {
-	options.beforeScenario = append(options.beforeScenario, f)
-	return options
+// WithBeforeScenario configures functions that should be executed before every scenario
+func WithBeforeScenario(f func()) func(*SuiteOptions) {
+	return func(options *SuiteOptions) {
+		options.beforeScenario = append(options.beforeScenario, f)
+	}
 }
 
-// Configures functions that should be executed after every scenario
-func (options SuiteOptions) WithAfterScenario(f func()) SuiteOptions {
-	options.afterScenario = append(options.afterScenario, f)
-	return options
+// WithAfterScenario configures functions that should be executed after every scenario
+func WithAfterScenario(f func()) func(*SuiteOptions) {
+	return func(options *SuiteOptions) {
+		options.afterScenario = append(options.afterScenario, f)
+	}
 }
 
-// Configures which tags should be skipped while executing a suite
+// WithIgnoredTags configures which tags should be skipped while executing a suite
 // Every tag has to start with @ otherwise will be ignored
-func (options SuiteOptions) WithIgnoredTags(tags []string) SuiteOptions {
-	options.ignoreTags = tags
-	return options
+func WithIgnoredTags(tags []string) func(*SuiteOptions) {
+	return func(options *SuiteOptions) {
+		options.ignoreTags = tags
+	}
 }
 
 type stepDef struct {
@@ -103,7 +109,12 @@ type TestingT interface {
 }
 
 // Creates a new suites with given configuration and empty steps defined
-func NewSuite(t TestingT, options SuiteOptions) *Suite {
+func NewSuite(t TestingT, optionClosures ...func(*SuiteOptions)) *Suite {
+	options := NewSuiteOptions()
+	for _, oC := range optionClosures {
+		oC(&options)
+	}
+
 	return &Suite{
 		t:       t,
 		steps:   []stepDef{},
@@ -347,7 +358,7 @@ func (s *Suite) runStep(ctx context.Context, t *testing.T, step *messages.Gherki
 
 	def, err := s.findStepDef(step.Text)
 	if err != nil {
-		t.Errorf("cannot find step definition for step: %s %s", step.Keyword, step.Text)
+		t.Errorf("cannot find step definition for step: %s%s", step.Keyword, step.Text)
 		return ctx
 	}
 


Test patch:
diff --git a/docs/testhttp.md b/docs/testhttp.md
index e937d6e..cd96305 100644
--- a/docs/testhttp.md
+++ b/docs/testhttp.md
@@ -9,7 +9,7 @@ This package is designed to help you testing your handlers and the domain hidden
 To be able to use it, you have to initialise using `testhttp.Setup()` function.
 
 ```golang
-s := NewSuite(t, NewSuiteOptions().WithFeaturesPath("features/http.feature"))
+s := NewSuite(t, WithFeaturesPath("features/http.feature"))
 router := http.NewServeMux()
 testhttp.Build(s, router)
 
diff --git a/gobdd_test.go b/gobdd_test.go
index a6fc30d..69085d1 100644
--- a/gobdd_test.go
+++ b/gobdd_test.go
@@ -11,7 +11,7 @@ import (
 )
 
 func TestScenarios(t *testing.T) {
-	suite := NewSuite(t, NewSuiteOptions().WithFeaturesPath("features/example.feature"))
+	suite := NewSuite(t, WithFeaturesPath("features/example.feature"))
 	suite.AddStep(`I add (\d+) and (\d+)`, add)
 	suite.AddStep(`the result should equal (\d+)`, check)
 
@@ -19,7 +19,7 @@ func TestScenarios(t *testing.T) {
 }
 
 func TestDifferentFuncTypes(t *testing.T) {
-	suite := NewSuite(t, NewSuiteOptions().WithFeaturesPath("features/func_types.feature"))
+	suite := NewSuite(t, WithFeaturesPath("features/func_types.feature"))
 	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)
 
@@ -27,7 +27,7 @@ func TestDifferentFuncTypes(t *testing.T) {
 }
 
 func TestScenarioOutline(t *testing.T) {
-	suite := NewSuite(t, NewSuiteOptions().WithFeaturesPath("features/outline.feature"))
+	suite := NewSuite(t, WithFeaturesPath("features/outline.feature"))
 	suite.AddStep(`I add (\d+) and (\d+)`, add)
 	suite.AddStep(`the result should equal <result>`, check)
 
@@ -35,8 +35,7 @@ func TestScenarioOutline(t *testing.T) {
 }
 
 func TestBackground(t *testing.T) {
-	options := NewSuiteOptions().WithFeaturesPath("features/background.feature")
-	suite := NewSuite(t, options)
+	suite := NewSuite(t, WithFeaturesPath("features/background.feature"))
 	suite.AddStep(`I add (\d+) and (\d+)`, add)
 	suite.AddStep(`the result should equal (\d+)`, check)
 
@@ -44,8 +43,7 @@ func TestBackground(t *testing.T) {
 }
 
 func TestTags(t *testing.T) {
-	options := NewSuiteOptions().WithFeaturesPath("features/tags.feature").WithTags([]string{"@tag"})
-	suite := NewSuite(t, options)
+	suite := NewSuite(t, WithFeaturesPath("features/tags.feature"), WithTags([]string{"@tag"}))
 	suite.AddStep(`fail the test`, fail)
 	suite.AddStep(`the test should pass`, pass)
 
@@ -53,9 +51,7 @@ func TestTags(t *testing.T) {
 }
 
 func TestIgnoredTags(t *testing.T) {
-	options := NewSuiteOptions().WithFeaturesPath("features/ignored_tags.feature")
-	options = options.WithIgnoredTags([]string{"@ignore"})
-	suite := NewSuite(t, options)
+	suite := NewSuite(t, WithFeaturesPath("features/ignored_tags.feature"), WithIgnoredTags([]string{"@ignore"}))
 	suite.AddStep(`fail the test`, fail)
 	suite.Run()
 }
@@ -74,7 +70,7 @@ func TestInvalidFunctionSignature(t *testing.T) {
 	for name, testCase := range testCases {
 		t.Run(name, func(t *testing.T) {
 			tester := &mockTester{}
-			suite := NewSuite(tester, NewSuiteOptions())
+			suite := NewSuite(tester)
 			suite.AddStep("", testCase.f)
 			suite.Run()
 			err := assert.Equals(1, tester.fatalCalled)
diff --git a/testhttp/requests_test.go b/testhttp/requests_test.go
index 036eadd..1300cb7 100644
--- a/testhttp/requests_test.go
+++ b/testhttp/requests_test.go
@@ -10,7 +10,7 @@ import (
 )
 
 func TestValidMethods(t *testing.T) {
-	s := gobdd.NewSuite(t, gobdd.NewSuiteOptions())
+	s := gobdd.NewSuite(t)
 	handler := Build(s, testHandler{})
 	methods := []string{"Get", "Post", "Trace", "Options", "Head", "Connect", "Patch", "Put", "Delete"}
 
diff --git a/testhttp_test.go b/testhttp_test.go
index 337d91a..52e9a98 100644
--- a/testhttp_test.go
+++ b/testhttp_test.go
@@ -10,7 +10,7 @@ import (
 )
 
 func TestHTTP(t *testing.T) {
-	s := NewSuite(t, NewSuiteOptions().WithFeaturesPath("features/http.feature"))
+	s := NewSuite(t, WithFeaturesPath("features/http.feature"))
 	router := http.NewServeMux()
 	router.HandleFunc("/health", func(w http.ResponseWriter, req *http.Request) {
 		w.WriteHeader(http.StatusOK)