Repository: go-bdd/gobdd
Pull request #47 (2019-10-27 12:02:01)

remove context package


Patch:
diff --git a/.travis.yml b/.travis.yml
index e3fdb45..cf005ca 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -39,7 +39,6 @@ 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
deleted file mode 100644
index 2a6d794..0000000
--- a/context/context.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package context
-
-import (
-	"fmt"
-)
-
-// Holds two types of data:
-//
-// * data saved by previously executed steps
-type Context struct {
-	values map[interface{}]interface{}
-	params [][]byte
-}
-
-func New() Context {
-	return Context{
-		values: map[interface{}]interface{}{},
-		params: [][]byte{},
-	}
-}
-
-func (ctx Context) GetParam(i int) interface{} {
-	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %d does not exist", i))
-	}
-
-	return ctx.params[i]
-}
-
-func (ctx Context) Set(key interface{}, value interface{}) {
-	ctx.values[key] = value
-}
-
-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 %+v does not exist", key))
-	}
-
-	return ctx.values[key]
-}
diff --git a/context/get.go b/context/get.go
deleted file mode 100644
index a96f651..0000000
--- a/context/get.go
+++ /dev/null
@@ -1,176 +0,0 @@
-// 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 %+v 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 %+v 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 %+v 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 %+v 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 %+v 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 %+v 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 %+v 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 %+v 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 %+v 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/gen.go b/gen.go
deleted file mode 100644
index af835de..0000000
--- a/gen.go
+++ /dev/null
@@ -1,3 +0,0 @@
-//go:generate go run gen/ctxget.go
-
-package gobdd
diff --git a/gen/ctxget.go b/gen/ctxget.go
deleted file mode 100644
index 468899d..0000000
--- a/gen/ctxget.go
+++ /dev/null
@@ -1,163 +0,0 @@
-//+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/get.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/get_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 %+v 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 7379d0b..ec18924 100644
--- a/go.mod
+++ b/go.mod
@@ -6,5 +6,4 @@ require (
 	github.com/cucumber/gherkin-go v5.1.0+incompatible
 	github.com/go-bdd/assert v0.0.0-20190820124234-20d47a68475d
 	github.com/logrusorgru/aurora v0.0.0-20190428105938-cea283e61946
-	github.com/stretchr/testify v1.4.0
 )
diff --git a/go.sum b/go.sum
index c1fb69b..528f110 100644
--- a/go.sum
+++ b/go.sum
@@ -1,17 +1,6 @@
 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/go-bdd/assert v0.0.0-20190820124234-20d47a68475d h1:zQazu3kApPoajWmXj9zFpCNE+UDefwwFRijKjzvHNCM=
 github.com/go-bdd/assert v0.0.0-20190820124234-20d47a68475d/go.mod h1:dOoqt7g2I/fpR7/Pyz0P19J3xjDj5lsHn3v9EaFLRjM=
 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/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 dc24c82..3cfe762 100644
--- a/gobdd.go
+++ b/gobdd.go
@@ -2,6 +2,7 @@ package gobdd
 
 import (
 	"bufio"
+	"context"
 	"errors"
 	"fmt"
 	"io/ioutil"
@@ -15,7 +16,6 @@ import (
 	"testing"
 
 	"github.com/cucumber/gherkin-go"
-	"github.com/go-bdd/gobdd/context"
 )
 
 // Holds all the information about the suite (options, steps to execute etc)
@@ -189,7 +189,7 @@ func (s *Suite) runFeature(feature *gherkin.Feature) error {
 					t.Log(fmt.Sprintf("Skipping scenario %s", scenario.Name))
 					continue
 				}
-				ctx := context.New()
+				ctx := context.Background()
 				err := s.runScenario(ctx, scenario, bkgSteps, t)
 				if err != nil {
 					hasErrors = true
@@ -202,7 +202,7 @@ func (s *Suite) runFeature(feature *gherkin.Feature) error {
 					continue
 				}
 
-				ctx := context.New()
+				ctx := context.Background()
 				if bkgSteps != nil {
 					s.runSteps(ctx, t, bkgSteps.Steps)
 				}
@@ -239,7 +239,7 @@ func (s *Suite) runScenarioOutline(outline *gherkin.ScenarioOutline, t *testing.
 			groups := ex.TableBody
 
 			for _, group := range groups {
-				ctx := context.New()
+				ctx := context.Background()
 				steps := s.runOutlineStep(outline, placeholders, group)
 				s.runSteps(ctx, t, steps)
 			}
@@ -342,20 +342,22 @@ func (s *Suite) runScenario(ctx context.Context, scenario *gherkin.Scenario, bkg
 	t.Run(scenario.Name, func(t *testing.T) {
 		if bkg != nil {
 			steps := s.getBackgroundSteps(bkg)
-			s.runSteps(ctx, t, steps)
+			ctx = s.runSteps(ctx, t, steps)
 		}
 		s.runSteps(ctx, t, scenario.Steps)
 	})
 	return nil
 }
 
-func (s *Suite) runSteps(ctx context.Context, t *testing.T, steps []*gherkin.Step) {
+func (s *Suite) runSteps(ctx context.Context, t *testing.T, steps []*gherkin.Step) context.Context {
 	for _, step := range steps {
-		s.runStep(ctx, t, step)
+		ctx = s.runStep(ctx, t, step)
 	}
+
+	return ctx
 }
 
-func (s *Suite) runStep(ctx context.Context, t *testing.T, step *gherkin.Step) {
+func (s *Suite) runStep(ctx context.Context, t *testing.T, step *gherkin.Step) context.Context {
 	defer func() {
 		if r := recover(); r != nil {
 			t.Error(r)
@@ -365,7 +367,7 @@ func (s *Suite) runStep(ctx context.Context, t *testing.T, step *gherkin.Step) {
 	def, err := s.findStepDef(step.Text)
 	if err != nil {
 		t.Errorf("cannot find step definition for step: %s %s", step.Keyword, step.Text)
-		return
+		return ctx
 	}
 
 	params := def.expr.FindSubmatch([]byte(step.Text))[1:]
@@ -387,13 +389,18 @@ func (s *Suite) runStep(ctx context.Context, t *testing.T, step *gherkin.Step) {
 			paramType := s.paramType(v, inType)
 			in = append(in, paramType)
 		}
-		v := d.Call(in)[0]
+		retValues := d.Call(in)
+		v := retValues[1]
 
 		if !v.IsNil() {
 			err = v.Interface().(error)
 			t.Errorf(step.Keyword, step.Text, err)
 		}
+
+		ctx = retValues[0].Interface().(context.Context)
 	})
+
+	return ctx
 }
 
 func (s *Suite) paramType(param []byte, inType reflect.Type) reflect.Value {
diff --git a/steps.go b/steps.go
index a1d5e56..c49a38a 100644
--- a/steps.go
+++ b/steps.go
@@ -1,8 +1,8 @@
 package gobdd
 
 import (
+	"context"
 	"errors"
-	"github.com/go-bdd/gobdd/context"
 	"reflect"
 )
 
@@ -12,10 +12,16 @@ func validateStepFunc(f interface{}) error {
 		return errors.New("the parameter should be a function")
 	}
 
-	if value.Type().NumOut() != 1 {
-		return errors.New("the function should return only error")
+	if value.Type().NumOut() != 2 {
+		return errors.New("the function should return the context.Context and error")
 	}
 	val := value.Type().Out(0)
+	contextInterface  := reflect.TypeOf((*context.Context)(nil)).Elem()
+	if !val.Implements(contextInterface) {
+		return errors.New("the returned value should implement the context.Context interface")
+	}
+
+	val = value.Type().Out(1)
 	errorInterface  := reflect.TypeOf((*error)(nil)).Elem()
 	if !val.Implements(errorInterface) {
 		return errors.New("the returned value should implement the Error interface")
@@ -26,7 +32,7 @@ func validateStepFunc(f interface{}) error {
 	}
 
 	val = value.Type().In(0)
-	n := val.ConvertibleTo(reflect.ValueOf(context.Context{}).Type())
+	n := val.ConvertibleTo(reflect.TypeOf((*context.Context)(nil)).Elem())
 	if !n {
 		return errors.New("the returned value should implement the Error interface")
 	}


Test patch:
diff --git a/context/get_test.go b/context/get_test.go
deleted file mode 100644
index 34804c8..0000000
--- a/context/get_test.go
+++ /dev/null
@@ -1,356 +0,0 @@
-// 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/gobdd_test.go b/gobdd_test.go
index 81790c3..78f1281 100644
--- a/gobdd_test.go
+++ b/gobdd_test.go
@@ -1,10 +1,9 @@
 package gobdd
 
 import (
+	"context"
 	"errors"
 	"testing"
-
-	"github.com/go-bdd/gobdd/context"
 )
 
 func TestScenarios(t *testing.T) {
@@ -118,42 +117,42 @@ func TestInvalidFunctionSignature(t *testing.T) {
 	}
 }
 
-func addf(ctx context.Context, var1, var2 float32) error {
+func addf(ctx context.Context, var1, var2 float32) (context.Context, error) {
 	res := var1 + var2
-	ctx.Set("sumRes", res)
-	return nil
+	ctx = context.WithValue(ctx, "sumRes", res)
+	return ctx, nil
 }
 
-func add(ctx context.Context, var1, var2 int) error {
+func add(ctx context.Context, var1, var2 int) (context.Context, error) {
 	res := var1 + var2
-	ctx.Set("sumRes", res)
-	return nil
+	ctx = context.WithValue(ctx, "sumRes", res)
+	return ctx, nil
 }
 
-func checkf(ctx context.Context, sum float32) error {
-	received := ctx.GetFloat32("sumRes")
+func checkf(ctx context.Context, sum float32) (context.Context, error) {
+	received := ctx.Value("sumRes")
 
 	if sum != received {
-		return errors.New("the math does not work for you")
+		return ctx, errors.New("the math does not work for you")
 	}
 
-	return nil
+	return ctx, nil
 }
 
-func check(ctx context.Context, sum int) error {
-	received := ctx.GetInt("sumRes")
+func check(ctx context.Context, sum int) (context.Context, error) {
+	received := ctx.Value("sumRes")
 
 	if sum != received {
-		return errors.New("the math does not work for you")
+		return ctx, errors.New("the math does not work for you")
 	}
 
-	return nil
+	return ctx, nil
 }
 
-func fail(context.Context) error {
-	return errors.New("the step should never be executed")
+func fail(ctx context.Context) (context.Context, error) {
+	return ctx, errors.New("the step should never be executed")
 }
 
-func pass(context.Context) error {
-	return nil
+func pass(ctx context.Context) (context.Context, error) {
+	return ctx, nil
 }
diff --git a/steps_test.go b/steps_test.go
new file mode 100644
index 0000000..e6571b5
--- /dev/null
+++ b/steps_test.go
@@ -0,0 +1,30 @@
+package gobdd
+
+import (
+	"context"
+	"testing"
+)
+
+func TestValidateStepFunc(t *testing.T) {
+	testCases:= map[string]interface{}{
+		"function without arguments": func()(context.Context, error){ return nil, nil},
+		"function with invalid first arguments": func(int)(context.Context, error){ return nil, nil},
+		"function without returned values": func(context.Context){},
+		"function with invalid first returned value": func(context.Context)(int, error){ return 0, nil},
+		"function with invalid second returned value": func(context.Context)(context.Context, int){ return nil, 0},
+	}
+
+	for name, testCase := range testCases{
+		t.Run(name, func(t *testing.T) {
+			if err := validateStepFunc(testCase); err == nil {
+				t.Errorf("the test should fail for the function")
+			}
+		})
+	}
+}
+
+func TestValidateStepFunc_ValidFunction(t *testing.T) {
+	if err := validateStepFunc(func(context.Context) (context.Context, error) { return nil, nil}); err != nil {
+		t.Errorf("the test should NOT fail for the function: %s",err)
+	}
+}
\ No newline at end of file
diff --git a/testhttp/setup.go b/testhttp/setup.go
index 50b5577..508989b 100644
--- a/testhttp/setup.go
+++ b/testhttp/setup.go
@@ -2,11 +2,11 @@ package testhttp
 
 import (
 	"bytes"
+	"context"
 	"encoding/json"
 	"errors"
 	"fmt"
 	"github.com/go-bdd/assert"
-	"github.com/go-bdd/gobdd/context"
 	"io/ioutil"
 	"net/http"
 )
@@ -43,106 +43,106 @@ func Build(addStep addStepper, h httpHandler) TestHTTP {
 	return thhtp
 }
 
-func (t testHTTPMethods) iSetRequestBodyTo(ctx context.Context, body string) error {
-	r := ctx.Get(RequestKey{}).(*http.Request)
+func (t testHTTPMethods) iSetRequestBodyTo(ctx context.Context, body string) (context.Context, error) {
+	r := ctx.Value(RequestKey{}).(*http.Request)
 	r.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(body)))
-	ctx.Set(RequestKey{}, r)
-	return nil
+	ctx = context.WithValue(ctx, RequestKey{}, r)
+	return ctx, nil
 }
 
-func (t testHTTPMethods) iSetRequestSetTo(ctx context.Context, headerName, value string) error {
-	req := ctx.Get(RequestKey{}).(*http.Request)
+func (t testHTTPMethods) iSetRequestSetTo(ctx context.Context, headerName, value string) (context.Context, error) {
+	req := ctx.Value(RequestKey{}).(*http.Request)
 	req.Header.Add(headerName, value)
 
-	ctx.Set(RequestKey{}, req)
-	return nil
+	ctx = context.WithValue(ctx, RequestKey{}, req)
+	return ctx, nil
 }
 
-func (t testHTTPMethods) responseHeaderEquals(ctx context.Context, headerName, expected string) error {
-	resp := ctx.Get(ResponseKey{}).(Response)
+func (t testHTTPMethods) responseHeaderEquals(ctx context.Context, headerName, expected string) (context.Context, error) {
+	resp := ctx.Value(ResponseKey{}).(Response)
 	given := resp.Header.Get(headerName)
 
-	return assert.Equals(expected, given)
+	return ctx, assert.Equals(expected, given)
 }
 
-func (t testHTTPMethods) theRequestHasBody(ctx context.Context, body string) error {
-	req := ctx.Get(RequestKey{}).(*http.Request)
+func (t testHTTPMethods) theRequestHasBody(ctx context.Context, body string) (context.Context, error) {
+	req := ctx.Value(RequestKey{}).(*http.Request)
 	req.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(body)))
-	ctx.Set(RequestKey{}, req)
-	return nil
+	ctx = context.WithValue(ctx, RequestKey{}, req)
+	return ctx, nil
 }
 
-func (t testHTTPMethods) iHaveARequest(ctx context.Context, method, url string) error {
+func (t testHTTPMethods) iHaveARequest(ctx context.Context, method, url string) (context.Context, error) {
 	req, err := http.NewRequest(method, url, nil)
 	if err != nil {
-		return err
+		return ctx, err
 	}
 
-	ctx.Set(RequestKey{}, req)
-	return nil
+	ctx = context.WithValue(ctx, RequestKey{}, req)
+	return ctx, nil
 }
 
-func (t testHTTPMethods) theResponseIs(ctx context.Context, expectedResponse string) error {
-	resp := ctx.Get(ResponseKey{}).(Response)
+func (t testHTTPMethods) theResponseIs(ctx context.Context, expectedResponse string) (context.Context, error) {
+	resp := ctx.Value(ResponseKey{}).(Response)
 	body, err := ioutil.ReadAll(resp.Body)
 	resp.Body = ioutil.NopCloser(bytes.NewBuffer(body))
-	ctx.Set(ResponseKey{}, resp)
+	ctx = context.WithValue(ctx, ResponseKey{}, resp)
 	if err != nil {
-		return fmt.Errorf("an error while reading the body: %s", err)
+		return ctx, fmt.Errorf("an error while reading the body: %s", err)
 	}
 
 	if expectedResponse != string(body) {
-		return errors.New("the body doesn't contain a valid JSON")
+		return ctx, errors.New("the body doesn't contain a valid JSON")
 	}
-	return nil
+	return ctx, nil
 }
 
-func (t testHTTPMethods) validJSON(ctx context.Context) error {
-	resp := ctx.Get(ResponseKey{}).(Response)
+func (t testHTTPMethods) validJSON(ctx context.Context) (context.Context, error) {
+	resp := ctx.Value(ResponseKey{}).(Response)
 	body, err := ioutil.ReadAll(resp.Body)
 	resp.Body = ioutil.NopCloser(bytes.NewBuffer(body))
-	ctx.Set(ResponseKey{}, resp)
+	ctx = context.WithValue(ctx, ResponseKey{}, resp)
 	if err != nil {
-		return fmt.Errorf("an error while reading the body: %s", err)
+		return ctx, fmt.Errorf("an error while reading the body: %s", err)
 	}
 
 	if !json.Valid(body) {
-		return errors.New("the body doesn't contain a valid JSON")
+		return ctx, errors.New("the body doesn't contain a valid JSON")
 	}
-	return nil
+	return ctx, nil
 }
 
-func (t testHTTPMethods) iMakeRequest(ctx context.Context) error {
-	req := ctx.Get(RequestKey{}).(*http.Request)
+func (t testHTTPMethods) iMakeRequest(ctx context.Context) (context.Context, error) {
+	req := ctx.Value(RequestKey{}).(*http.Request)
 	resp, err := t.tHTTP.MakeRequest(req)
 	if err != nil {
-		return err
+		return ctx, err
 	}
 
-	ctx.Set(ResponseKey{}, resp)
-	return nil
+	ctx = context.WithValue(ctx, ResponseKey{}, resp)
+	return ctx, nil
 }
 
-func (t testHTTPMethods) makeRequest(ctx context.Context, method, url string) error {
+func (t testHTTPMethods) makeRequest(ctx context.Context, method, url string) (context.Context, error) {
 	req, err := http.NewRequest(method, url, nil)
 	if err != nil {
-		return err
+		return ctx, err
 	}
 
 	resp, err := t.tHTTP.MakeRequest(req)
 	if err != nil {
-		return err
+		return ctx, err
 	}
 
-	ctx.Set(ResponseKey{}, resp)
-	return nil
+	ctx = context.WithValue(ctx, ResponseKey{}, resp)
+	return ctx, nil
 }
 
-func (t testHTTPMethods) statusCodeEquals(ctx context.Context, expectedStatus int) error {
-	resp := ctx.Get(ResponseKey{}).(Response)
+func (t testHTTPMethods) statusCodeEquals(ctx context.Context, expectedStatus int) (context.Context, error) {
+	resp := ctx.Value(ResponseKey{}).(Response)
 
 	if expectedStatus != resp.Code {
-		return fmt.Errorf("expected status code: %d but %d given", expectedStatus, resp.Code)
+		return ctx, fmt.Errorf("expected status code: %d but %d given", expectedStatus, resp.Code)
 	}
-	return nil
+	return ctx, nil
 }
diff --git a/testhttp_test.go b/testhttp_test.go
index 142985e..70eebc9 100644
--- a/testhttp_test.go
+++ b/testhttp_test.go
@@ -1,8 +1,8 @@
 package gobdd
 
 import (
+	"context"
 	"github.com/go-bdd/assert"
-	"github.com/go-bdd/gobdd/context"
 	"net/http"
 	"testing"
 
@@ -40,30 +40,30 @@ func TestHTTP(t *testing.T) {
 	s.Run()
 }
 
-func requestHasMethodSetTo(ctx context.Context, method string) error {
-	r := ctx.Get(testhttp.RequestKey{}).(*http.Request)
-	return assert.Equals(method, r.Method)
+func requestHasMethodSetTo(ctx context.Context, method string) (context.Context, error) {
+	r := ctx.Value(testhttp.RequestKey{}).(*http.Request)
+	return ctx, assert.Equals(method, r.Method)
 }
 
-func requestBodyIsNil(ctx context.Context) error {
-	r := ctx.Get(testhttp.RequestKey{}).(*http.Request)
-	return assert.Nil(r.Body)
+func requestBodyIsNil(ctx context.Context) (context.Context, error) {
+	r := ctx.Value(testhttp.RequestKey{}).(*http.Request)
+	return ctx, assert.Nil(r.Body)
 }
 
-func urlIsSetTo(ctx context.Context, url string) error {
-	r := ctx.Get(testhttp.RequestKey{}).(*http.Request)
-	return assert.Equals(url, r.URL.String())
+func urlIsSetTo(ctx context.Context, url string) (context.Context, error) {
+	r := ctx.Value(testhttp.RequestKey{}).(*http.Request)
+	return ctx, assert.Equals(url, r.URL.String())
 }
 
-func ISetHeaderTo(ctx context.Context, headerName, value string) error {
-	r := ctx.Get(testhttp.RequestKey{}).(*http.Request)
+func ISetHeaderTo(ctx context.Context, headerName, value string) (context.Context, error) {
+	r := ctx.Value(testhttp.RequestKey{}).(*http.Request)
 	r.Header.Set(headerName, value)
-	ctx.Set(testhttp.RequestKey{}, r)
-	return nil
+	ctx = context.WithValue(ctx, testhttp.RequestKey{}, r)
+	return ctx, nil
 }
 
-func requestHasHeaderSetTo(ctx context.Context, headerName,expected  string) error {
-	r := ctx.Get(testhttp.RequestKey{}).(*http.Request)
+func requestHasHeaderSetTo(ctx context.Context, headerName,expected  string) (context.Context, error) {
+	r := ctx.Value(testhttp.RequestKey{}).(*http.Request)
 	given := r.Header.Get(headerName)
-	return assert.Equals(expected, given)
+	return ctx, assert.Equals(expected, given)
 }