Repository: go-bdd/gobdd
Pull request #101 (2020-05-15 17:02:11)

Fix issue with scenario outline
We had an issue where only first example in feature outline was executed. This fixes it as well as refactoring the way they are created.


Patch:
diff --git a/context/context.go b/context/context.go
index ebad33b..62fa9f3 100644
--- a/context/context.go
+++ b/context/context.go
@@ -16,6 +16,19 @@ func New() Context {
 	}
 }
 
+// Clone creates a copy of the context
+func (ctx Context) Clone() Context {
+	c := Context{
+		values: map[interface{}]interface{}{},
+	}
+
+	for k, v := range ctx.values {
+		c.Set(k, v)
+	}
+
+	return c
+}
+
 // Sets the value under the key
 func (ctx Context) Set(key interface{}, value interface{}) {
 	ctx.values[key] = value
diff --git a/go.mod b/go.mod
index 60ca666..f423d32 100644
--- a/go.mod
+++ b/go.mod
@@ -3,9 +3,8 @@ module github.com/go-bdd/gobdd
 go 1.12
 
 require (
-	github.com/cucumber/gherkin-go/v9 v9.2.0
-	github.com/cucumber/messages-go/v9 v9.0.3
+	github.com/cucumber/gherkin-go/v13 v13.0.0
+	github.com/cucumber/messages-go/v12 v12.0.0
 	github.com/go-bdd/assert v0.0.0-20190820124234-20d47a68475d
-	github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381
-	github.com/stretchr/testify v1.4.0
+	github.com/stretchr/testify v1.5.1
 )
diff --git a/go.sum b/go.sum
index 4f8e389..f3f152e 100644
--- a/go.sum
+++ b/go.sum
@@ -1,8 +1,9 @@
 github.com/aslakhellesoy/gox v1.0.100/go.mod h1:AJl542QsKKG96COVsv0N74HHzVQgDIQPceVUh1aeU2M=
-github.com/cucumber/gherkin-go/v9 v9.2.0 h1:vxpzP4JtfNSDGH4s0u4TIxv+RaX533MCD+XNakz5kLY=
-github.com/cucumber/gherkin-go/v9 v9.2.0/go.mod h1:W/+Z5yOowYWXRMlC6lJvM9LFDAFfsicZ1sstjPKfWWQ=
-github.com/cucumber/messages-go/v9 v9.0.3 h1:xXYjyj2aUOdkakEJAQIvP+1Bn2gOQNN+pY5pCRZQZzI=
-github.com/cucumber/messages-go/v9 v9.0.3/go.mod h1:TICon2O2emBWMY1eeQvog6b+zK5c+puAFO6avjzC/JA=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/cucumber/gherkin-go/v13 v13.0.0 h1:d09AwPZldyOFlCADAIZQMt7T+VHCrGdp5106+BCIfZU=
+github.com/cucumber/gherkin-go/v13 v13.0.0/go.mod h1:pzMEPEIPcPOBDkE8HaWC+Ck6eDBtBmIWVksUkSin/2E=
+github.com/cucumber/messages-go/v12 v12.0.0 h1:ZlZYZrYDDc35zCe0HK2y95GUcxHHqr8yB9kdrXCjnzU=
+github.com/cucumber/messages-go/v12 v12.0.0/go.mod h1:5zuJu21U6rB+BBqyGoQr839a4h4GUElPnSozdHyhCOQ=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -15,25 +16,24 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
 github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
-github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 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/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
-gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/gobdd.go b/gobdd.go
index d9044d8..d7fbc7d 100644
--- a/gobdd.go
+++ b/gobdd.go
@@ -14,9 +14,8 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/cucumber/gherkin-go/v9"
-	msgs "github.com/cucumber/messages-go/v9"
-
+	"github.com/cucumber/gherkin-go/v13"
+	msgs "github.com/cucumber/messages-go/v12"
 	"github.com/go-bdd/gobdd/context"
 )
 
@@ -270,62 +269,90 @@ func (s *Suite) runFeature(feature *msgs.GherkinDocument_Feature) error {
 	return nil
 }
 
-func (s *Suite) getOutlineStep(steps []*msgs.GherkinDocument_Feature_Step,
+func (s *Suite) getOutlineStep(
+	steps []*msgs.GherkinDocument_Feature_Step,
 	examples []*msgs.GherkinDocument_Feature_Scenario_Examples) []*msgs.GherkinDocument_Feature_Step {
-	var newSteps []*msgs.GherkinDocument_Feature_Step
+	stepsList := make([][]*msgs.GherkinDocument_Feature_Step, len(steps))
 
-	for _, outlineStep := range steps {
+	for i, outlineStep := range steps {
 		for _, example := range examples {
-			newSteps = append(newSteps, s.stepsFromExample(outlineStep, example)...)
+			stepsList[i] = append(stepsList[i], s.stepsFromExamples(outlineStep, example)...)
+		}
+	}
+
+	var newSteps []*msgs.GherkinDocument_Feature_Step
+
+	if len(stepsList) == 0 {
+		return newSteps
+	}
+
+	for ei := range examples {
+		for ci := range examples[ei].TableBody {
+			for si := range steps {
+				newSteps = append(newSteps, stepsList[si][ci])
+			}
 		}
 	}
 
 	return newSteps
 }
 
-func (s *Suite) stepsFromExample(sourceStep *msgs.GherkinDocument_Feature_Step,
+func (s *Suite) stepsFromExamples(
+	sourceStep *msgs.GherkinDocument_Feature_Step,
 	example *msgs.GherkinDocument_Feature_Scenario_Examples) []*msgs.GherkinDocument_Feature_Step {
 	steps := []*msgs.GherkinDocument_Feature_Step{}
-	text := sourceStep.GetText()
+
 	placeholders := example.GetTableHeader().GetCells()
+	placeholdersValues := []string{}
 
-	for i, placeholder := range placeholders {
+	for _, placeholder := range placeholders {
 		ph := "<" + placeholder.GetValue() + ">"
-		index := strings.Index(text, ph)
-		originalText := text
+		placeholdersValues = append(placeholdersValues, ph)
+	}
 
-		if index == -1 {
+	text := sourceStep.GetText()
+
+	for _, row := range example.GetTableBody() {
+		// iterate over the cells and update the text
+		stepText, expr := s.stepFromExample(text, row, placeholdersValues)
+
+		// find step definition for the new step
+		def, err := s.findStepDef(stepText)
+		if err != nil {
 			continue
 		}
 
-		for _, row := range example.GetTableBody() {
-			value := row.GetCells()[i].GetValue()
-			text = strings.Replace(text, "<"+placeholder.Value+">", value, -1)
-			t := getRegexpForVar(value)
+		// add the step to the list
+		s.AddStep(expr, def.f)
 
-			def, err := s.findStepDef(originalText)
-			if err != nil {
-				continue
-			}
-
-			expr := strings.Replace(def.expr.String(), ph, t, -1)
-			s.AddStep(expr, def.f)
+		// clone a step
+		step := &msgs.GherkinDocument_Feature_Step{
+			Location: sourceStep.Location,
+			Keyword:  sourceStep.Keyword,
+			Text:     stepText,
+			Argument: sourceStep.Argument,
 		}
-	}
 
-	// clone a step
-	step := &msgs.GherkinDocument_Feature_Step{
-		Location: sourceStep.Location,
-		Keyword:  sourceStep.Keyword,
-		Text:     text,
-		Argument: sourceStep.Argument,
+		steps = append(steps, step)
 	}
 
-	steps = append(steps, step)
-
 	return steps
 }
 
+func (s *Suite) stepFromExample(
+	stepName string,
+	row *msgs.GherkinDocument_Feature_TableRow, placeholders []string) (string, string) {
+	expr := stepName
+
+	for i, ph := range placeholders {
+		t := getRegexpForVar(row.Cells[i].Value)
+		expr = strings.Replace(expr, ph, t, -1)
+		stepName = strings.Replace(stepName, ph, row.Cells[i].Value, -1)
+	}
+
+	return stepName, expr
+}
+
 func (s *Suite) callBeforeScenarios(ctx context.Context) {
 	for _, f := range s.options.beforeScenario {
 		f(ctx)
@@ -350,9 +377,13 @@ func (s *Suite) runScenario(ctx context.Context, scenario *msgs.GherkinDocument_
 		}
 		steps := scenario.Steps
 		if examples := scenario.GetExamples(); len(examples) > 0 {
+			c := ctx.Clone()
 			steps = s.getOutlineStep(scenario.GetSteps(), examples)
+			s.runSteps(c, t, steps)
+		} else {
+			c := ctx.Clone()
+			s.runSteps(c, t, steps)
 		}
-		s.runSteps(ctx, t, steps)
 	})
 }
 


Test patch:
diff --git a/gobdd_test.go b/gobdd_test.go
index 11ee2cf..53ef03a 100644
--- a/gobdd_test.go
+++ b/gobdd_test.go
@@ -6,8 +6,8 @@ import (
 	"regexp"
 	"testing"
 
+	msgs "github.com/cucumber/messages-go/v12"
 	"github.com/go-bdd/assert"
-
 	"github.com/go-bdd/gobdd/context"
 )
 
@@ -40,11 +40,49 @@ func TestDifferentFuncTypes(t *testing.T) {
 func TestScenarioOutline(t *testing.T) {
 	suite := NewSuite(t, WithFeaturesPath("features/outline.feature"))
 	suite.AddStep(`I add (\d+) and (\d+)`, add)
-	suite.AddStep(`the result should equal <result>`, check)
+	suite.AddStep(`the result should equal (\d+)`, check)
 
 	suite.Run()
 }
 
+func TestScenarioOutlineExecutesAllTests(t *testing.T) {
+	c := 0
+	suite := NewSuite(t, WithFeaturesPath("features/outline.feature"))
+	suite.AddStep(`I add (\d+) and (\d+)`, add)
+	suite.AddStep(`the result should equal (\d+)`, func(t StepTest, ctx context.Context, sum int) {
+		c++
+		check(t, ctx, sum)
+	})
+
+	suite.Run()
+
+	if err := assert.Equals(2, c); err != nil {
+		t.Errorf("expected to run %d times but %d got", 2, c)
+	}
+}
+
+func TestStepFromExample(t *testing.T) {
+	s := NewSuite(t)
+	st, expr := s.stepFromExample("I add <d1> and <d2>", &msgs.GherkinDocument_Feature_TableRow{
+		Cells: []*msgs.GherkinDocument_Feature_TableRow_TableCell{
+			{Value: "1"},
+			{Value: "2"},
+		},
+	}, []string{"<d1>", "<d2>"})
+
+	if err := assert.NotNil(st); err != nil {
+		t.Error(err)
+	}
+
+	if err := assert.Equals("I add 1 and 2", st); err != nil {
+		t.Error(err)
+	}
+
+	if err := assert.Equals(`I add (\d+) and (\d+)`, expr); err != nil {
+		t.Error(err)
+	}
+}
+
 func TestBackground(t *testing.T) {
 	suite := NewSuite(t, WithFeaturesPath("features/background.feature"))
 	suite.AddStep(`I add (\d+) and (\d+)`, add)
@@ -167,15 +205,12 @@ func checkf(t StepTest, ctx context.Context, sum float32) {
 func check(t StepTest, ctx context.Context, sum int) {
 	received, err := ctx.Get("sumRes")
 	if err != nil {
-		t.Error(err.Error())
-
+		t.Error(err)
 		return
 	}
 
 	if sum != received {
-		t.Error("the math does not work for you")
-
-		return
+		t.Errorf("expected %d but %d received", sum, received)
 	}
 }