Repository: go-bdd/gobdd
Pull request #37 (2019-08-27 12:12:56)

Add optional parameters to Get* functions
The usage will be:

```go
// will fail when the param is not defined
requiredParam := ctx.GetInt(something{})
```

```go
def := 123
// will never panic
optionalParam := ctx.GetInt(something{}, def)
```


Patch:
diff --git a/.travis.yml b/.travis.yml
index cf005ca..e3fdb45 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -39,6 +39,7 @@ before_script:
 # failing test, we want to see both. Configure golangci-lint with a
 # .golangci.yml file at the top level of your repo.
 script:
+  - go generate
   - golangci-lint run       # run a bunch of code checkers/linters in parallel
   - go test -v -race ./...  # Run all the tests with the race detector enabled
 
diff --git a/context/context.go b/context/context.go
index 5b51262..7cba35d 100644
--- a/context/context.go
+++ b/context/context.go
@@ -133,131 +133,30 @@ func (ctx Context) GetInt64Param(i int) int64 {
 	}
 
 	data := ctx.params[i]
-	param, err := strconv.ParseInt(string(data), 10, 32)
+	param, err := strconv.ParseInt(string(data), 10, 64)
 
 	if err != nil {
 		panic(err)
 	}
 
-	return int64(param)
+	return param
 }
 
 func (ctx Context) Set(key interface{}, value interface{}) {
 	ctx.values[key] = value
 }
 
-func (ctx Context) Get(key interface{}) interface{} {
+func (ctx Context) Get(key interface{}, defaultValue ...interface{}) interface{} {
 	if _, ok := ctx.values[key]; !ok {
+		if len(defaultValue) == 1 {
+			return defaultValue[0]
+		}
 		panic(fmt.Sprintf("the key %s does not exist", key))
 	}
 
 	return ctx.values[key]
 }
 
-func (ctx Context) GetString(key interface{}) string {
-	if _, ok := ctx.values[key]; !ok {
-		panic(fmt.Sprintf("the key %s does not exist", key))
-	}
-
-	value, ok := ctx.values[key].(string)
-	if !ok {
-		panic(fmt.Sprintf("the expected value is not string (%T)", key))
-	}
-
-	return value
-}
-
-func (ctx Context) GetFloat64(key interface{}) float64 {
-	if _, ok := ctx.values[key]; !ok {
-		panic(fmt.Sprintf("the key %s does not exist", key))
-	}
-
-	value, ok := ctx.values[key].(float64)
-	if !ok {
-		panic(fmt.Sprintf("the expected value is not float64 (%T)", key))
-	}
-
-	return value
-}
-
-func (ctx Context) GetFloat32(key interface{}) float32 {
-	if _, ok := ctx.values[key]; !ok {
-		panic(fmt.Sprintf("the key %s does not exist", key))
-	}
-
-	value, ok := ctx.values[key].(float32)
-	if !ok {
-		panic(fmt.Sprintf("the expected value is not float32 (%T)", key))
-	}
-
-	return value
-}
-
-func (ctx Context) GetInt(key interface{}) int {
-	if _, ok := ctx.values[key]; !ok {
-		panic(fmt.Sprintf("the key %s does not exist", key))
-	}
-
-	value, ok := ctx.values[key].(int)
-	if !ok {
-		panic(fmt.Sprintf("the expected value is not int (%T)", key))
-	}
-
-	return value
-}
-
-func (ctx Context) GetInt8(key interface{}) int8 {
-	if _, ok := ctx.values[key]; !ok {
-		panic(fmt.Sprintf("the key %s does not exist", key))
-	}
-
-	value, ok := ctx.values[key].(int8)
-	if !ok {
-		panic(fmt.Sprintf("the expected value is not int8 (%T)", key))
-	}
-
-	return value
-}
-
-func (ctx Context) GetInt16(key interface{}) int16 {
-	if _, ok := ctx.values[key]; !ok {
-		panic(fmt.Sprintf("the key %s does not exist", key))
-	}
-
-	value, ok := ctx.values[key].(int16)
-	if !ok {
-		panic(fmt.Sprintf("the expected value is not int12 (%T)", key))
-	}
-
-	return value
-}
-
-func (ctx Context) GetInt32(key interface{}) int32 {
-	if _, ok := ctx.values[key]; !ok {
-		panic(fmt.Sprintf("the key %s does not exist", key))
-	}
-
-	value, ok := ctx.values[key].(int32)
-	if !ok {
-		panic(fmt.Sprintf("the expected value is not int32 (%T)", key))
-	}
-
-	return value
-}
-
-func (ctx Context) GetInt64(key interface{}) int64 {
-	if _, ok := ctx.values[key]; !ok {
-		panic(fmt.Sprintf("the key %s does not exist", key))
-	}
-
-	value, ok := ctx.values[key].(int64)
-	if !ok {
-		panic(fmt.Sprintf("the expected value is not int64 (%T)", key))
-	}
-
-	return value
-}
-
 func (ctx *Context) SetParams(params [][]byte) {
 	ctx.params = params
 }
diff --git a/context/getparams.go b/context/getparams.go
new file mode 100644
index 0000000..c74dad2
--- /dev/null
+++ b/context/getparams.go
@@ -0,0 +1,176 @@
+// Code generated .* DO NOT EDIT.
+package context
+
+import "fmt"
+
+func (ctx Context) GetString(key interface{}, defaultValue ...string) string {
+	if len(defaultValue) > 1 {
+        panic(fmt.Sprintf("allowed to pass only 1 default value but %d got", len(defaultValue)))
+    }
+	if _, ok := ctx.values[key]; !ok {
+		if len(defaultValue) == 1 {
+			return defaultValue[0]
+		}
+		panic(fmt.Sprintf("the key %s does not exist", key))
+	}
+
+	value, ok := ctx.values[key].(string)
+	if !ok {
+		panic(fmt.Sprintf("the expected value is not string (%T)", key))
+	}
+
+	return value
+}
+
+func (ctx Context) GetInt(key interface{}, defaultValue ...int) int {
+	if len(defaultValue) > 1 {
+        panic(fmt.Sprintf("allowed to pass only 1 default value but %d got", len(defaultValue)))
+    }
+	if _, ok := ctx.values[key]; !ok {
+		if len(defaultValue) == 1 {
+			return defaultValue[0]
+		}
+		panic(fmt.Sprintf("the key %s does not exist", key))
+	}
+
+	value, ok := ctx.values[key].(int)
+	if !ok {
+		panic(fmt.Sprintf("the expected value is not int (%T)", key))
+	}
+
+	return value
+}
+
+func (ctx Context) GetInt8(key interface{}, defaultValue ...int8) int8 {
+	if len(defaultValue) > 1 {
+        panic(fmt.Sprintf("allowed to pass only 1 default value but %d got", len(defaultValue)))
+    }
+	if _, ok := ctx.values[key]; !ok {
+		if len(defaultValue) == 1 {
+			return defaultValue[0]
+		}
+		panic(fmt.Sprintf("the key %s does not exist", key))
+	}
+
+	value, ok := ctx.values[key].(int8)
+	if !ok {
+		panic(fmt.Sprintf("the expected value is not int8 (%T)", key))
+	}
+
+	return value
+}
+
+func (ctx Context) GetInt16(key interface{}, defaultValue ...int16) int16 {
+	if len(defaultValue) > 1 {
+        panic(fmt.Sprintf("allowed to pass only 1 default value but %d got", len(defaultValue)))
+    }
+	if _, ok := ctx.values[key]; !ok {
+		if len(defaultValue) == 1 {
+			return defaultValue[0]
+		}
+		panic(fmt.Sprintf("the key %s does not exist", key))
+	}
+
+	value, ok := ctx.values[key].(int16)
+	if !ok {
+		panic(fmt.Sprintf("the expected value is not int16 (%T)", key))
+	}
+
+	return value
+}
+
+func (ctx Context) GetInt32(key interface{}, defaultValue ...int32) int32 {
+	if len(defaultValue) > 1 {
+        panic(fmt.Sprintf("allowed to pass only 1 default value but %d got", len(defaultValue)))
+    }
+	if _, ok := ctx.values[key]; !ok {
+		if len(defaultValue) == 1 {
+			return defaultValue[0]
+		}
+		panic(fmt.Sprintf("the key %s does not exist", key))
+	}
+
+	value, ok := ctx.values[key].(int32)
+	if !ok {
+		panic(fmt.Sprintf("the expected value is not int32 (%T)", key))
+	}
+
+	return value
+}
+
+func (ctx Context) GetInt64(key interface{}, defaultValue ...int64) int64 {
+	if len(defaultValue) > 1 {
+        panic(fmt.Sprintf("allowed to pass only 1 default value but %d got", len(defaultValue)))
+    }
+	if _, ok := ctx.values[key]; !ok {
+		if len(defaultValue) == 1 {
+			return defaultValue[0]
+		}
+		panic(fmt.Sprintf("the key %s does not exist", key))
+	}
+
+	value, ok := ctx.values[key].(int64)
+	if !ok {
+		panic(fmt.Sprintf("the expected value is not int64 (%T)", key))
+	}
+
+	return value
+}
+
+func (ctx Context) GetFloat32(key interface{}, defaultValue ...float32) float32 {
+	if len(defaultValue) > 1 {
+        panic(fmt.Sprintf("allowed to pass only 1 default value but %d got", len(defaultValue)))
+    }
+	if _, ok := ctx.values[key]; !ok {
+		if len(defaultValue) == 1 {
+			return defaultValue[0]
+		}
+		panic(fmt.Sprintf("the key %s does not exist", key))
+	}
+
+	value, ok := ctx.values[key].(float32)
+	if !ok {
+		panic(fmt.Sprintf("the expected value is not float32 (%T)", key))
+	}
+
+	return value
+}
+
+func (ctx Context) GetFloat64(key interface{}, defaultValue ...float64) float64 {
+	if len(defaultValue) > 1 {
+        panic(fmt.Sprintf("allowed to pass only 1 default value but %d got", len(defaultValue)))
+    }
+	if _, ok := ctx.values[key]; !ok {
+		if len(defaultValue) == 1 {
+			return defaultValue[0]
+		}
+		panic(fmt.Sprintf("the key %s does not exist", key))
+	}
+
+	value, ok := ctx.values[key].(float64)
+	if !ok {
+		panic(fmt.Sprintf("the expected value is not float64 (%T)", key))
+	}
+
+	return value
+}
+
+func (ctx Context) GetBool(key interface{}, defaultValue ...bool) bool {
+	if len(defaultValue) > 1 {
+        panic(fmt.Sprintf("allowed to pass only 1 default value but %d got", len(defaultValue)))
+    }
+	if _, ok := ctx.values[key]; !ok {
+		if len(defaultValue) == 1 {
+			return defaultValue[0]
+		}
+		panic(fmt.Sprintf("the key %s does not exist", key))
+	}
+
+	value, ok := ctx.values[key].(bool)
+	if !ok {
+		panic(fmt.Sprintf("the expected value is not bool (%T)", key))
+	}
+
+	return value
+}
+
diff --git a/features/tags.feature b/features/tags.feature
index 847cd6f..7f53005 100644
--- a/features/tags.feature
+++ b/features/tags.feature
@@ -2,6 +2,9 @@ Feature: ignored tags
   @tag
   Scenario: the scenario should be pass
     Then the test should pass
-
+  @tag
+  Scenario Outline: the scenario should be pass
+    Then the test should pass
+    Examples:
   Scenario: the test should never be executed
     Then fail the test
\ No newline at end of file
diff --git a/gen.go b/gen.go
new file mode 100644
index 0000000..d33cb89
--- /dev/null
+++ b/gen.go
@@ -0,0 +1,3 @@
+//go:generate go run gen/ctxparams.go
+
+package gobdd
diff --git a/gen/ctxparams.go b/gen/ctxparams.go
new file mode 100644
index 0000000..e477e56
--- /dev/null
+++ b/gen/ctxparams.go
@@ -0,0 +1,163 @@
+//+build ignore
+
+package main
+
+import (
+	"html/template"
+	"log"
+	"os"
+	"strings"
+)
+
+type typeDef struct {
+	Name  string
+	Value string
+}
+
+func noescape(str string) template.HTML {
+	return template.HTML(str)
+}
+
+func main() {
+	funcMap := template.FuncMap{
+		"Title":    strings.Title,
+		"noescape": noescape,
+	}
+
+	types := []typeDef{
+		{
+			Name:  "string",
+			Value: `"example text"`,
+		},
+		{
+			Name:  "int",
+			Value: "123",
+		},
+		{
+			Name:  "int8",
+			Value: "123",
+		},
+		{
+			Name:  "int16",
+			Value: "123",
+		},
+		{
+			Name:  "int32",
+			Value: "123",
+		},
+		{
+			Name:  "int64",
+			Value: "123",
+		},
+		{
+			Name:  "float32",
+			Value: "123.5",
+		},
+		{
+			Name:  "float64",
+			Value: "123.5",
+		},
+		{
+			Name:  "bool",
+			Value: "false",
+		},
+	}
+
+	f, err := os.Create("context/getparams.go")
+	die(err)
+
+	var tmpl = template.Must(template.New("").Funcs(funcMap).Parse(getTmpl))
+
+	err = tmpl.Execute(f, struct {
+		Types []typeDef
+	}{Types: types})
+	die(err)
+	f.Close()
+
+	f, err = os.Create("context/getparams_test.go")
+	die(err)
+	tmpl = template.Must(template.New("").Funcs(funcMap).Parse(testTmpl))
+	err = tmpl.Execute(f, struct {
+		Types []typeDef
+	}{Types: types})
+	die(err)
+	f.Close()
+}
+
+func die(err error) {
+	if err != nil {
+		log.Fatal(err)
+	}
+}
+
+var testTmpl = `// Code generated .* DO NOT EDIT.
+package context
+
+import "testing"
+{{ range .Types }}
+func TestContext_Get{{ .Name | Title }}(t *testing.T) {
+	ctx := New()
+	expected := {{ .Name }}({{ .Value | noescape }})
+	ctx.Set("test", expected)
+	received := ctx.Get{{ .Name | Title }}("test")
+	if received != expected {
+		t.Errorf("expected %+v but received %+v", expected, received)
+	}
+}
+
+func TestContext_Get{{ .Name | Title }}_WithDefaultValue(t *testing.T) {
+	ctx := New()
+	defaultValue := {{ .Name }}({{ .Value | noescape }})
+	received := ctx.Get{{ .Name | Title }}("test", defaultValue)
+	if received != defaultValue {
+		t.Errorf("expected %+v but received %+v", defaultValue, received)
+	}
+}
+
+func TestContext_Get{{ .Name | Title }}_PanicOnMoreThanOneDefaultValue(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the Get{{ .Name | Title }} should panic")
+        }
+    }()
+	_ = ctx.Get{{ .Name | Title }}("test", {{ .Value | noescape }}, {{ .Value | noescape }})
+}
+
+func TestContext_Get{{ .Name | Title }}_PanicOnNotFound(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the Get{{ .Name | Title }} should panic")
+        }
+    }()
+	_ = ctx.Get{{ .Name | Title }}("test")
+}
+{{ end }}
+`
+
+var getTmpl = `// Code generated .* DO NOT EDIT.
+package context
+
+import "fmt"
+{{ range .Types }}
+func (ctx Context) Get{{ .Name | Title }}(key interface{}, defaultValue ...{{ .Name }}) {{ .Name }} {
+	if len(defaultValue) > 1 {
+        panic(fmt.Sprintf("allowed to pass only 1 default value but %d got", len(defaultValue)))
+    }
+	if _, ok := ctx.values[key]; !ok {
+		if len(defaultValue) == 1 {
+			return defaultValue[0]
+		}
+		panic(fmt.Sprintf("the key %s does not exist", key))
+	}
+
+	value, ok := ctx.values[key].({{ .Name }})
+	if !ok {
+		panic(fmt.Sprintf("the expected value is not {{ .Name }} (%T)", key))
+	}
+
+	return value
+}
+{{ end }}
+`
diff --git a/go.mod b/go.mod
index 148cb53..21491c2 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,5 @@ go 1.12
 require (
 	github.com/cucumber/gherkin-go v5.1.0+incompatible
 	github.com/logrusorgru/aurora v0.0.0-20190428105938-cea283e61946
-	github.com/mattn/goveralls v0.0.2 // indirect
-	golang.org/x/tools v0.0.0-20190730215328-ed3277de2799 // indirect
+	github.com/stretchr/testify v1.4.0
 )
diff --git a/go.sum b/go.sum
index 414d084..637ff64 100644
--- a/go.sum
+++ b/go.sum
@@ -1,13 +1,15 @@
 github.com/cucumber/gherkin-go v5.1.0+incompatible h1:RCvyVI6KQLI2IJkijZBeJcE4K3U7DnhQ1RjD7VV+AIk=
 github.com/cucumber/gherkin-go v5.1.0+incompatible/go.mod h1:bYJ65F+CDEAL70FXAu7/ef4ayC/NhRXO8zEW3IB21w0=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 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/mattn/goveralls v0.0.2 h1:7eJB6EqsPhRVxvwEXGnqdO2sJI0PTsrWoTMXEk9/OQc=
-github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/tools v0.0.0-20190730215328-ed3277de2799 h1:rvNf5qrBjmtxebJHK+blZSkGIv+Yg6UlDnl2ApkB6m4=
-golang.org/x/tools v0.0.0-20190730215328-ed3277de2799/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
+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=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/gobdd.go b/gobdd.go
index 1d1ec71..3ce536e 100644
--- a/gobdd.go
+++ b/gobdd.go
@@ -164,17 +164,23 @@ func (s *Suite) executeFeature(file string) error {
 
 func (s *Suite) runFeature(feature *gherkin.Feature) error {
 	log.SetOutput(ioutil.Discard)
-	r := reporter.NewFmt()
+	r := reporter.NewReporter()
+	_ = r.Report(feature)
 	hasErrors := false
 	var bkgSteps *gherkin.Background
 
 	for _, child := range feature.Children {
 		if scenario, ok := child.(*gherkin.Scenario); ok {
 			if s.skipScenario(scenario.Tags) {
-				r.SkippedScenario(scenario)
+				r.Skip(scenario)
 				continue
 			}
-			err := s.runScenario(scenario, bkgSteps, r)
+			ctx := context.New()
+
+			if bkgSteps != nil {
+				s.runSteps(ctx, r, bkgSteps.Steps)
+			}
+			err := s.runScenario(ctx, scenario, bkgSteps, r)
 			if err != nil {
 				hasErrors = true
 			}
@@ -182,11 +188,16 @@ func (s *Suite) runFeature(feature *gherkin.Feature) error {
 
 		if scenario, ok := child.(*gherkin.ScenarioOutline); ok {
 			if s.skipScenario(scenario.Tags) {
-				r.SkippedScenarioOutline(scenario)
+				r.Skip(scenario)
 				continue
 			}
 
-			err := s.runScenarioOutline(scenario, bkgSteps, r)
+			ctx := context.New()
+			if bkgSteps != nil {
+				s.runSteps(ctx, r, bkgSteps.Steps)
+			}
+
+			err := s.runScenarioOutline(scenario, r)
 			if err != nil {
 				hasErrors = true
 			}
@@ -194,11 +205,11 @@ func (s *Suite) runFeature(feature *gherkin.Feature) error {
 
 		if bkg, ok := child.(*gherkin.Background); ok {
 			bkgSteps = bkg
+			_ = r.Report(bkg)
 		}
 	}
 
 	r.GenerateReport()
-
 	if hasErrors {
 		return errors.New("the feature contains errors")
 	}
@@ -206,10 +217,10 @@ func (s *Suite) runFeature(feature *gherkin.Feature) error {
 	return nil
 }
 
-func (s *Suite) runScenarioOutline(outline *gherkin.ScenarioOutline, bkg *gherkin.Background, reporter reporter.Reporter) error {
+func (s *Suite) runScenarioOutline(outline *gherkin.ScenarioOutline, reporter *reporter.Reporter) error {
 	s.callBeforeScenarios()
 	defer s.callAfterScenarios()
-	reporter.ScenarioOutline(outline)
+	_ = reporter.Report(outline)
 	for _, ex := range outline.Examples {
 		if len(ex.TableBody) == 0 {
 			continue
@@ -220,12 +231,6 @@ func (s *Suite) runScenarioOutline(outline *gherkin.ScenarioOutline, bkg *gherki
 
 		for _, group := range groups {
 			ctx := context.New()
-			if bkg != nil {
-				reporter.Background(bkg)
-				steps := s.getBackgroundSteps(bkg)
-				s.runSteps(ctx, reporter, steps)
-			}
-
 			steps := s.runOutlineStep(outline, placeholders, group)
 			s.runSteps(ctx, reporter, steps)
 		}
@@ -320,15 +325,13 @@ func (s *Suite) outlineDataTableArguments(t *gherkin.DataTable, placeholders []*
 	return tbl
 }
 
-func (s *Suite) runScenario(scenario *gherkin.Scenario, bkg *gherkin.Background, reporter reporter.Reporter) error {
+func (s *Suite) runScenario(ctx context.Context, scenario *gherkin.Scenario, bkg *gherkin.Background, reporter *reporter.Reporter) error {
 	s.callBeforeScenarios()
 
 	defer s.callAfterScenarios()
-	reporter.Scenario(scenario)
-	ctx := context.New()
+	_ = reporter.Report(scenario)
 
 	if bkg != nil {
-		reporter.Background(bkg)
 		steps := s.getBackgroundSteps(bkg)
 		s.runSteps(ctx, reporter, steps)
 	}
@@ -337,13 +340,13 @@ func (s *Suite) runScenario(scenario *gherkin.Scenario, bkg *gherkin.Background,
 	return nil
 }
 
-func (s *Suite) runSteps(ctx context.Context, reporter reporter.Reporter, steps []*gherkin.Step) {
+func (s *Suite) runSteps(ctx context.Context, reporter *reporter.Reporter, steps []*gherkin.Step) {
 	for _, step := range steps {
 		s.runStep(ctx, reporter, step)
 	}
 }
 
-func (s *Suite) runStep(ctx context.Context, reporter reporter.Reporter, step *gherkin.Step) {
+func (s *Suite) runStep(ctx context.Context, reporter *reporter.Reporter, step *gherkin.Step) {
 	defer func() {
 		if r := recover(); r != nil {
 			s.t.Error(r)
@@ -352,7 +355,7 @@ func (s *Suite) runStep(ctx context.Context, reporter reporter.Reporter, step *g
 
 	def, err := s.findStepDef(step.Text)
 	if err != nil {
-		reporter.UndefinedStep(step)
+		reporter.Undefined(step)
 		s.t.Errorf("cannot find step definition for step '%s'", step.Text)
 		return
 	}
@@ -361,10 +364,10 @@ func (s *Suite) runStep(ctx context.Context, reporter reporter.Reporter, step *g
 	ctx.SetParams(b[1:])
 	err = def.f(ctx)
 	if err != nil {
-		reporter.FailedStep(step, err)
+		reporter.Failed(step, err)
 		s.t.Error(err)
 	} else {
-		reporter.SucceededStep(step)
+		_ = reporter.Report(step)
 	}
 }
 
diff --git a/reporter/fmt.go b/reporter/fmt.go
index f40f286..bec3a44 100644
--- a/reporter/fmt.go
+++ b/reporter/fmt.go
@@ -2,104 +2,105 @@ package reporter
 
 import (
 	"fmt"
-	"time"
 
 	"github.com/cucumber/gherkin-go"
 	. "github.com/logrusorgru/aurora"
 )
 
-func NewFmt() *fmtReporter {
-	return &fmtReporter{
-		timer:          time.Now(),
-		scenarios:      []interface{}{},
-		undefinedSteps: []*gherkin.Step{},
-		succeededSteps: []*gherkin.Step{},
-		failedSteps:    []*gherkin.Step{},
-	}
+type fmtReporter struct {
 }
 
-type fmtReporter struct {
-	timer                   time.Time
-	scenarios               []interface{} // *gherkin.Scenario or *gherkin.ScenarioOutline
-	skippedScenarios        []*gherkin.Scenario
-	skippedScenarioOutlines []*gherkin.ScenarioOutline
-	undefinedSteps          []*gherkin.Step
-	succeededSteps          []*gherkin.Step
-	failedSteps             []*gherkin.Step
+func (r fmtReporter) Step(step *gherkin.Step) {
+	fmt.Printf("   %s %s\n", Yellow(step.Keyword), Green(step.Text))
 }
 
-func (r *fmtReporter) Scenario(scenario *gherkin.Scenario) {
+func (r fmtReporter) Scenario(scenario *gherkin.Scenario) {
 	if len(scenario.Tags) != 0 {
 		for _, tag := range scenario.Tags {
-			fmt.Printf("%s\n", Yellow(tag.Name))
+			fmt.Printf(" %s\n", Yellow(tag.Name))
 		}
 	}
-
-	fmt.Printf("%s: %s\n", scenario.Keyword, Gray(10, scenario.Name))
-	r.scenarios = append(r.scenarios, scenario)
+	fmt.Printf(" %s: %s\n", Yellow(scenario.Keyword), Green(scenario.Name))
 }
 
-func (r *fmtReporter) SkippedScenario(scenario *gherkin.Scenario) {
+func (r fmtReporter) ScenarioOutline(scenario *gherkin.ScenarioOutline) {
 	if len(scenario.Tags) != 0 {
 		for _, tag := range scenario.Tags {
-			fmt.Printf("%s\n", Yellow(tag.Name))
+			fmt.Printf(" %s\n", Yellow(tag.Name))
 		}
 	}
+	fmt.Printf(" %s: %s\n", Yellow(scenario.Keyword), Green(scenario.Name))
 
-	fmt.Printf("%s: %s\n", scenario.Keyword, Gray(10, scenario.Name))
-	r.skippedScenarios = append(r.skippedScenarios, scenario)
-}
+	for _, example := range scenario.Examples {
+		fmt.Printf(" %s:\n", Yellow(example.Keyword))
+	}
 
-func (r *fmtReporter) SkippedScenarioOutline(scenario *gherkin.ScenarioOutline) {
-	if len(scenario.Tags) != 0 {
-		for _, tag := range scenario.Tags {
-			fmt.Printf("%s\n", Yellow(tag.Name))
-		}
+	for _, step := range scenario.Steps {
+		fmt.Printf("     %s %s\n", Yellow(step.Keyword), Green(step.Text))
 	}
+}
 
-	fmt.Printf("%s: %s\n", scenario.Keyword, Blue(scenario.Name))
-	r.skippedScenarioOutlines = append(r.skippedScenarioOutlines, scenario)
+func (r fmtReporter) Undefined(s *gherkin.Step) {
+	fmt.Printf("   %s %s\n", Red("Undefined step"), Yellow(s.Text))
 }
 
-func (r *fmtReporter) Background(bkg *gherkin.Background) {
-	fmt.Printf("%s: %s\n", bkg.Keyword, Green(bkg.Name))
+func (r fmtReporter) Skip(s *gherkin.Step) {
+	fmt.Printf(" %s %s\n", s.Keyword, Gray(10, s.Text))
 }
 
-func (r *fmtReporter) UndefinedStep(step *gherkin.Step) {
-	fmt.Printf("  %s: %s\n", Red("Undefined step"), Yellow(step.Text))
-	r.undefinedSteps = append(r.undefinedSteps, step)
+func (r fmtReporter) Failed(step *gherkin.Step, e error) {
+	fmt.Printf("   %s %s\n", step.Keyword, Red(step.Text))
 }
 
-func (r *fmtReporter) SucceededStep(step *gherkin.Step) {
-	fmt.Printf("  %s: %s\n", Yellow(step.Keyword), Green(step.Text))
-	r.succeededSteps = append(r.succeededSteps, step)
+func (r fmtReporter) Background(background *gherkin.Background) {
+	fmt.Printf("  %s %s\n", Yellow(background.Keyword), Green(background.Name))
+	for _, s := range background.Steps {
+		r.Step(s)
+	}
 }
 
-func (r *fmtReporter) FailedStep(step *gherkin.Step, err error) {
-	fmt.Printf("  %s: %s\n", step.Keyword, Red(step.Text))
-	r.failedSteps = append(r.failedSteps, step)
+func (r fmtReporter) Feature(feature *gherkin.Feature) {
+	fmt.Printf("%s %s\n", Yellow(feature.Keyword), Green(feature.Name))
 }
 
-func (r *fmtReporter) ScenarioOutline(outline *gherkin.ScenarioOutline) {
-	if len(outline.Tags) != 0 {
-		for _, tag := range outline.Tags {
-			fmt.Printf("%s\n", Yellow(tag.Name))
+func (r fmtReporter) SkipScenario(s *gherkin.Scenario) {
+	if len(s.Tags) != 0 {
+		for _, tag := range s.Tags {
+			fmt.Printf(" %s\n", Yellow(tag.Name))
 		}
 	}
-	fmt.Printf("%s: %s\n", outline.Keyword, Green(outline.Name))
-	r.scenarios = append(r.scenarios, outline)
+	fmt.Printf(" %s: %s\n", Gray(10, s.Keyword), Gray(10, s.Name))
+	for _, step := range s.Steps {
+		r.Skip(step)
+	}
+}
+
+func (r fmtReporter) SkipScenarioOutline(s *gherkin.ScenarioOutline) {
+	if len(s.Tags) != 0 {
+		for _, tag := range s.Tags {
+			fmt.Printf(" %s\n", Yellow(tag.Name))
+		}
+	}
+	for _, example := range s.Examples {
+		fmt.Printf(" %s:\n", Gray(10, example.Keyword))
+	}
+
+	fmt.Printf(" %s: %s\n", Gray(10, s.Keyword), Gray(10, s.Name))
+	for _, step := range s.Steps {
+		r.Skip(step)
+	}
 }
 
-func (r fmtReporter) GenerateReport() {
-	fmt.Printf("%d scenarios\n", len(r.scenarios))
-	fmt.Printf("%d steps\n", len(r.succeededSteps))
-	if c := len(r.failedSteps); c > 0 {
-		fmt.Printf("%d failed steps\n", len(r.failedSteps))
+func (r fmtReporter) Report(report executionReport) {
+	fmt.Printf("%d scenarios\n", report.countScenarios)
+	fmt.Printf("%d steps\n", report.countSteps)
+	if c := report.countFailedSteps; c > 0 {
+		fmt.Printf("%d failed steps\n", report.countFailedSteps)
 	}
 
-	if c := len(r.undefinedSteps); c > 0 {
-		fmt.Printf("%d undefined steps\n", len(r.undefinedSteps))
+	if c := report.countUndefined; c > 0 {
+		fmt.Printf("%d undefined steps\n", report.countUndefined)
 	}
 
-	fmt.Printf("took %s\n", time.Since(r.timer).String())
+	fmt.Printf("took %s\n", report.timeTook.String())
 }
diff --git a/reporter/reporter.go b/reporter/reporter.go
index 81b3ec8..749850b 100644
--- a/reporter/reporter.go
+++ b/reporter/reporter.go
@@ -1,12 +1,135 @@
 package reporter
 
-import "github.com/cucumber/gherkin-go"
-
-type Reporter interface {
-	Scenario(scenario *gherkin.Scenario)
-	UndefinedStep(step *gherkin.Step)
-	SucceededStep(step *gherkin.Step)
-	FailedStep(step *gherkin.Step, err error)
-	ScenarioOutline(outline *gherkin.ScenarioOutline)
-	Background(bkg *gherkin.Background)
+import (
+	"fmt"
+	"time"
+
+	"github.com/cucumber/gherkin-go"
+)
+
+func NewReporter() *Reporter {
+	return &Reporter{
+		timer:          time.Now(),
+		scenarios:      []interface{}{},
+		undefinedSteps: []*gherkin.Step{},
+		succeededSteps: []*gherkin.Step{},
+		failedSteps:    []*gherkin.Step{},
+		skippedSteps:   []*gherkin.Step{},
+		backgrounds:    []*gherkin.Background{},
+	}
+}
+
+type Reporter struct {
+	timer            time.Time
+	output           fmtReporter
+	features         []*gherkin.Feature
+	scenarios        []interface{} // *gherkin.Scenario or *gherkin.ScenarioOutline
+	skippedScenarios []interface{} // *gherkin.Scenario or *gherkin.ScenarioOutline
+	undefinedSteps   []*gherkin.Step
+	succeededSteps   []*gherkin.Step
+	failedSteps      []*gherkin.Step
+	skippedSteps     []*gherkin.Step
+	backgrounds      []*gherkin.Background
+}
+
+type executionReport struct {
+	countScenarios   int
+	countSteps       int
+	countFailedSteps int
+	countUndefined   int
+	timeTook         time.Duration
+}
+
+func (r *Reporter) Report(reported interface{}) error {
+	switch reported.(type) {
+	case *gherkin.Step:
+		s := reported.(*gherkin.Step)
+		r.output.Step(s)
+
+		r.succeededSteps = append(r.succeededSteps, s)
+	case *gherkin.Scenario:
+		s := reported.(*gherkin.Scenario)
+		r.output.Scenario(s)
+		r.scenarios = append(r.scenarios, s)
+	case *gherkin.ScenarioOutline:
+		s := reported.(*gherkin.ScenarioOutline)
+		r.output.ScenarioOutline(s)
+		r.scenarios = append(r.scenarios, s)
+
+	case *gherkin.Feature:
+		f := reported.(*gherkin.Feature)
+		r.output.Feature(f)
+		r.features = append(r.features, f)
+	case *gherkin.Background:
+		s := reported.(*gherkin.Background)
+		r.output.Background(s)
+
+		r.backgrounds = append(r.backgrounds, s)
+	default:
+		return fmt.Errorf("%T", reported)
+	}
+
+	return nil
+}
+
+func (r *Reporter) Undefined(s *gherkin.Step) {
+	r.output.Undefined(s)
+	r.undefinedSteps = append(r.undefinedSteps, s)
+}
+
+func (r *Reporter) Skip(reported interface{}) {
+	switch reported.(type) {
+	case *gherkin.Step:
+		s := reported.(*gherkin.Step)
+		r.output.Skip(s)
+
+		r.skippedSteps = append(r.skippedSteps, s)
+	case *gherkin.Scenario:
+		s := reported.(*gherkin.Scenario)
+		r.output.SkipScenario(s)
+		r.skippedScenarios = append(r.skippedScenarios, s)
+	case *gherkin.ScenarioOutline:
+		s := reported.(*gherkin.ScenarioOutline)
+		r.output.SkipScenarioOutline(s)
+		r.skippedScenarios = append(r.skippedScenarios, s)
+	default:
+		panic("should never happen")
+	}
+}
+
+func (r *Reporter) Failed(s *gherkin.Step, err error) {
+	r.output.Failed(s, err)
+	r.failedSteps = append(r.failedSteps, s)
+}
+
+func (r Reporter) GenerateReport() {
+	er := executionReport{
+		countScenarios:   r.countScenarios(),
+		countSteps:       r.countSteps(),
+		countFailedSteps: r.countFailed(),
+		countUndefined:   r.countUndefined(),
+		timeTook:         time.Since(r.timer),
+	}
+
+	r.output.Report(er)
+}
+
+func (r Reporter) countScenarios() int {
+	return len(r.scenarios)
+}
+
+func (r Reporter) countSteps() int {
+	return len(r.succeededSteps)
+}
+
+func (r *Reporter) countUndefined() int {
+	return len(r.undefinedSteps)
+}
+
+func (r *Reporter) countFailed() int {
+	return len(r.failedSteps)
+}
+
+func (r *Reporter) countSkippedSteps() int {
+	return len(r.skippedSteps)
 }


Test patch:
diff --git a/context/getparams_test.go b/context/getparams_test.go
new file mode 100644
index 0000000..34804c8
--- /dev/null
+++ b/context/getparams_test.go
@@ -0,0 +1,356 @@
+// Code generated .* DO NOT EDIT.
+package context
+
+import "testing"
+
+func TestContext_GetString(t *testing.T) {
+	ctx := New()
+	expected := string("example text")
+	ctx.Set("test", expected)
+	received := ctx.GetString("test")
+	if received != expected {
+		t.Errorf("expected %+v but received %+v", expected, received)
+	}
+}
+
+func TestContext_GetString_WithDefaultValue(t *testing.T) {
+	ctx := New()
+	defaultValue := string("example text")
+	received := ctx.GetString("test", defaultValue)
+	if received != defaultValue {
+		t.Errorf("expected %+v but received %+v", defaultValue, received)
+	}
+}
+
+func TestContext_GetString_PanicOnMoreThanOneDefaultValue(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the GetString should panic")
+        }
+    }()
+	_ = ctx.GetString("test", "example text", "example text")
+}
+
+func TestContext_GetString_PanicOnNotFound(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the GetString should panic")
+        }
+    }()
+	_ = ctx.GetString("test")
+}
+
+func TestContext_GetInt(t *testing.T) {
+	ctx := New()
+	expected := int(123)
+	ctx.Set("test", expected)
+	received := ctx.GetInt("test")
+	if received != expected {
+		t.Errorf("expected %+v but received %+v", expected, received)
+	}
+}
+
+func TestContext_GetInt_WithDefaultValue(t *testing.T) {
+	ctx := New()
+	defaultValue := int(123)
+	received := ctx.GetInt("test", defaultValue)
+	if received != defaultValue {
+		t.Errorf("expected %+v but received %+v", defaultValue, received)
+	}
+}
+
+func TestContext_GetInt_PanicOnMoreThanOneDefaultValue(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the GetInt should panic")
+        }
+    }()
+	_ = ctx.GetInt("test", 123, 123)
+}
+
+func TestContext_GetInt_PanicOnNotFound(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the GetInt should panic")
+        }
+    }()
+	_ = ctx.GetInt("test")
+}
+
+func TestContext_GetInt8(t *testing.T) {
+	ctx := New()
+	expected := int8(123)
+	ctx.Set("test", expected)
+	received := ctx.GetInt8("test")
+	if received != expected {
+		t.Errorf("expected %+v but received %+v", expected, received)
+	}
+}
+
+func TestContext_GetInt8_WithDefaultValue(t *testing.T) {
+	ctx := New()
+	defaultValue := int8(123)
+	received := ctx.GetInt8("test", defaultValue)
+	if received != defaultValue {
+		t.Errorf("expected %+v but received %+v", defaultValue, received)
+	}
+}
+
+func TestContext_GetInt8_PanicOnMoreThanOneDefaultValue(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the GetInt8 should panic")
+        }
+    }()
+	_ = ctx.GetInt8("test", 123, 123)
+}
+
+func TestContext_GetInt8_PanicOnNotFound(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the GetInt8 should panic")
+        }
+    }()
+	_ = ctx.GetInt8("test")
+}
+
+func TestContext_GetInt16(t *testing.T) {
+	ctx := New()
+	expected := int16(123)
+	ctx.Set("test", expected)
+	received := ctx.GetInt16("test")
+	if received != expected {
+		t.Errorf("expected %+v but received %+v", expected, received)
+	}
+}
+
+func TestContext_GetInt16_WithDefaultValue(t *testing.T) {
+	ctx := New()
+	defaultValue := int16(123)
+	received := ctx.GetInt16("test", defaultValue)
+	if received != defaultValue {
+		t.Errorf("expected %+v but received %+v", defaultValue, received)
+	}
+}
+
+func TestContext_GetInt16_PanicOnMoreThanOneDefaultValue(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the GetInt16 should panic")
+        }
+    }()
+	_ = ctx.GetInt16("test", 123, 123)
+}
+
+func TestContext_GetInt16_PanicOnNotFound(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the GetInt16 should panic")
+        }
+    }()
+	_ = ctx.GetInt16("test")
+}
+
+func TestContext_GetInt32(t *testing.T) {
+	ctx := New()
+	expected := int32(123)
+	ctx.Set("test", expected)
+	received := ctx.GetInt32("test")
+	if received != expected {
+		t.Errorf("expected %+v but received %+v", expected, received)
+	}
+}
+
+func TestContext_GetInt32_WithDefaultValue(t *testing.T) {
+	ctx := New()
+	defaultValue := int32(123)
+	received := ctx.GetInt32("test", defaultValue)
+	if received != defaultValue {
+		t.Errorf("expected %+v but received %+v", defaultValue, received)
+	}
+}
+
+func TestContext_GetInt32_PanicOnMoreThanOneDefaultValue(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the GetInt32 should panic")
+        }
+    }()
+	_ = ctx.GetInt32("test", 123, 123)
+}
+
+func TestContext_GetInt32_PanicOnNotFound(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the GetInt32 should panic")
+        }
+    }()
+	_ = ctx.GetInt32("test")
+}
+
+func TestContext_GetInt64(t *testing.T) {
+	ctx := New()
+	expected := int64(123)
+	ctx.Set("test", expected)
+	received := ctx.GetInt64("test")
+	if received != expected {
+		t.Errorf("expected %+v but received %+v", expected, received)
+	}
+}
+
+func TestContext_GetInt64_WithDefaultValue(t *testing.T) {
+	ctx := New()
+	defaultValue := int64(123)
+	received := ctx.GetInt64("test", defaultValue)
+	if received != defaultValue {
+		t.Errorf("expected %+v but received %+v", defaultValue, received)
+	}
+}
+
+func TestContext_GetInt64_PanicOnMoreThanOneDefaultValue(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the GetInt64 should panic")
+        }
+    }()
+	_ = ctx.GetInt64("test", 123, 123)
+}
+
+func TestContext_GetInt64_PanicOnNotFound(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the GetInt64 should panic")
+        }
+    }()
+	_ = ctx.GetInt64("test")
+}
+
+func TestContext_GetFloat32(t *testing.T) {
+	ctx := New()
+	expected := float32(123.5)
+	ctx.Set("test", expected)
+	received := ctx.GetFloat32("test")
+	if received != expected {
+		t.Errorf("expected %+v but received %+v", expected, received)
+	}
+}
+
+func TestContext_GetFloat32_WithDefaultValue(t *testing.T) {
+	ctx := New()
+	defaultValue := float32(123.5)
+	received := ctx.GetFloat32("test", defaultValue)
+	if received != defaultValue {
+		t.Errorf("expected %+v but received %+v", defaultValue, received)
+	}
+}
+
+func TestContext_GetFloat32_PanicOnMoreThanOneDefaultValue(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the GetFloat32 should panic")
+        }
+    }()
+	_ = ctx.GetFloat32("test", 123.5, 123.5)
+}
+
+func TestContext_GetFloat32_PanicOnNotFound(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the GetFloat32 should panic")
+        }
+    }()
+	_ = ctx.GetFloat32("test")
+}
+
+func TestContext_GetFloat64(t *testing.T) {
+	ctx := New()
+	expected := float64(123.5)
+	ctx.Set("test", expected)
+	received := ctx.GetFloat64("test")
+	if received != expected {
+		t.Errorf("expected %+v but received %+v", expected, received)
+	}
+}
+
+func TestContext_GetFloat64_WithDefaultValue(t *testing.T) {
+	ctx := New()
+	defaultValue := float64(123.5)
+	received := ctx.GetFloat64("test", defaultValue)
+	if received != defaultValue {
+		t.Errorf("expected %+v but received %+v", defaultValue, received)
+	}
+}
+
+func TestContext_GetFloat64_PanicOnMoreThanOneDefaultValue(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the GetFloat64 should panic")
+        }
+    }()
+	_ = ctx.GetFloat64("test", 123.5, 123.5)
+}
+
+func TestContext_GetFloat64_PanicOnNotFound(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the GetFloat64 should panic")
+        }
+    }()
+	_ = ctx.GetFloat64("test")
+}
+
+func TestContext_GetBool(t *testing.T) {
+	ctx := New()
+	expected := bool(false)
+	ctx.Set("test", expected)
+	received := ctx.GetBool("test")
+	if received != expected {
+		t.Errorf("expected %+v but received %+v", expected, received)
+	}
+}
+
+func TestContext_GetBool_WithDefaultValue(t *testing.T) {
+	ctx := New()
+	defaultValue := bool(false)
+	received := ctx.GetBool("test", defaultValue)
+	if received != defaultValue {
+		t.Errorf("expected %+v but received %+v", defaultValue, received)
+	}
+}
+
+func TestContext_GetBool_PanicOnMoreThanOneDefaultValue(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the GetBool should panic")
+        }
+    }()
+	_ = ctx.GetBool("test", false, false)
+}
+
+func TestContext_GetBool_PanicOnNotFound(t *testing.T) {
+	ctx := New()
+	defer func() {
+        if r := recover(); r == nil {
+            t.Error("the GetBool should panic")
+        }
+    }()
+	_ = ctx.GetBool("test")
+}
+
diff --git a/reporter/reporter_test.go b/reporter/reporter_test.go
new file mode 100644
index 0000000..409fffa
--- /dev/null
+++ b/reporter/reporter_test.go
@@ -0,0 +1,45 @@
+package reporter
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/cucumber/gherkin-go"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestReporter_Report(t *testing.T) {
+	step := &gherkin.Step{}
+	r := NewReporter()
+	err := r.Report(step)
+	assert.NoError(t, err)
+	assert.Equal(t, 1, r.countSteps())
+}
+
+func TestReporter_Report_Invalid_Type(t *testing.T) {
+	r := NewReporter()
+	err := r.Report("this shouldn't go here")
+	assert.Error(t, err)
+	assert.Equal(t, 0, r.countSteps())
+}
+
+func TestReporter_Undefined(t *testing.T) {
+	step := &gherkin.Step{}
+	r := NewReporter()
+	r.Undefined(step)
+	assert.Equal(t, 1, r.countUndefined())
+}
+
+func TestReporter_Failed(t *testing.T) {
+	step := &gherkin.Step{}
+	r := NewReporter()
+	r.Failed(step, errors.New("some error"))
+	assert.Equal(t, 1, r.countFailed())
+}
+
+func TestReporter_Skip_Step(t *testing.T) {
+	step := &gherkin.Step{}
+	r := NewReporter()
+	r.Skip(step)
+	assert.Equal(t, 1, r.countSkippedSteps())
+}