Repository: go-bdd/gobdd (go)

Issue:
Change interface of functions to accept steps' parameters
I'm thinking about changing the step's API a bit. Right now it's very simple.

```go
type Func func(ctx context.Context) error
```

Parameters from steps are fetched using methods like below:

```go
ctx.GetIntParam(0) -> returns the first parameter
ctx.GetIntParam(1) -> returns the second parameter
```

It works and was super easy to implement but operating on indexes isn't that user-friendly as it should be. I don't want to do much magic but there's an option to extend the interface to be more dynamic.
Using reflection, it's possible to dynamically pass those parameters to the function on the fly so users won't have to play with indexes.

```go
func myCoolStep(ctx Context, int myparam) error {}
// or... 
func myCoolStep(ctx Context, myparam int, anotherparam string) error {}
```

Thanks to this, the context can be simplified to contain only getters for context-based parameters.

```go
ctx.GetInt(userID{})
```

On the other hand, it put a bit more responsibility to users while changing/writing scenarios. The debugging can be a bit harder, too. IMO it's acceptable but I want to hear feedback from you.
 

Pull request description:
Change interface of functions to accept steps' parameters
Fixes #34 

Patch:
diff --git a/context/context.go b/context/context.go
index 761b014..2f799c3 100644
--- a/context/context.go
+++ b/context/context.go
@@ -43,6 +43,3 @@ func (ctx Context) Get(key interface{}, defaultValue ...interface{}) interface{}
 	return ctx.values[key]
 }
 
-func (ctx *Context) SetParams(params [][]byte) {
-	ctx.params = params
-}
diff --git a/context/getparam.go b/context/getparam.go
deleted file mode 100644
index 8902343..0000000
--- a/context/getparam.go
+++ /dev/null
@@ -1,136 +0,0 @@
-// Code generated .* DO NOT EDIT.
-package context
-
-import (
-	"fmt"
-	"strconv"
-)
-
-func (ctx Context) GetBoolParam(i int) bool {
-	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %+v does not exist", i))
-	}
-
-	v, err := strconv.ParseBool(string(ctx.params[i]))
-	if err != nil {
-		panic(fmt.Sprintf("cannot convert to bool"))
-	}
-
-	return v
-}
-
-func (ctx Context) GetStringParam(i int) string {
-	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %+v does not exist", i))
-	}
-
-	return string(ctx.params[i])
-}
-
-
-func (ctx Context) GetFloat32Param(i int) float32 {
-	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %+v does not exist", i))
-	}
-
-	data := ctx.params[i]
-	param, err := strconv.ParseFloat(string(data), 32)
-
-	if err != nil {
-		panic(err)
-	}
-
-	return float32(param)
-}
-
-func (ctx Context) GetFloat64Param(i int) float64 {
-	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %+v does not exist", i))
-	}
-
-	data := ctx.params[i]
-	param, err := strconv.ParseFloat(string(data), 64)
-
-	if err != nil {
-		panic(err)
-	}
-
-	return float64(param)
-}
-
-
-func (ctx Context) GetIntParam(i int) int {
-	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %+v does not exist", i))
-	}
-
-	data := ctx.params[i]
-	param, err := strconv.ParseInt(string(data), 10, 32)
-
-	if err != nil {
-		panic(err)
-	}
-
-	return int(param)
-}
-
-func (ctx Context) GetInt8Param(i int) int8 {
-	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %+v does not exist", i))
-	}
-
-	data := ctx.params[i]
-	param, err := strconv.ParseInt(string(data), 10, 8)
-
-	if err != nil {
-		panic(err)
-	}
-
-	return int8(param)
-}
-
-func (ctx Context) GetInt16Param(i int) int16 {
-	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %+v does not exist", i))
-	}
-
-	data := ctx.params[i]
-	param, err := strconv.ParseInt(string(data), 10, 16)
-
-	if err != nil {
-		panic(err)
-	}
-
-	return int16(param)
-}
-
-func (ctx Context) GetInt32Param(i int) int32 {
-	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %+v does not exist", i))
-	}
-
-	data := ctx.params[i]
-	param, err := strconv.ParseInt(string(data), 10, 32)
-
-	if err != nil {
-		panic(err)
-	}
-
-	return int32(param)
-}
-
-func (ctx Context) GetInt64Param(i int) int64 {
-	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %+v does not exist", i))
-	}
-
-	data := ctx.params[i]
-	param, err := strconv.ParseInt(string(data), 10, 64)
-
-	if err != nil {
-		panic(err)
-	}
-
-	return int64(param)
-}
-
diff --git a/gen.go b/gen.go
index df60315..af835de 100644
--- a/gen.go
+++ b/gen.go
@@ -1,4 +1,3 @@
 //go:generate go run gen/ctxget.go
-//go:generate go run gen/ctxgetparam.go
 
 package gobdd
diff --git a/gen/ctxgetparam.go b/gen/ctxgetparam.go
deleted file mode 100644
index 8bb3b31..0000000
--- a/gen/ctxgetparam.go
+++ /dev/null
@@ -1,212 +0,0 @@
-//+build ignore
-
-package main
-
-import (
-	"html/template"
-	"log"
-	"os"
-	"strings"
-)
-
-type typeDef struct {
-	Name  string
-	Value string
-	Bytes int
-}
-
-func noescape(str string) template.HTML {
-	return template.HTML(str)
-}
-
-func main() {
-	funcMap := template.FuncMap{
-		"Title":    strings.Title,
-		"noescape": noescape,
-	}
-
-	floatTypes := []typeDef{
-		{
-			Name:  "float32",
-			Value: "123.5",
-			Bytes: 32,
-		},
-		{
-			Name:  "float64",
-			Value: "123.5",
-			Bytes: 64,
-		},
-	}
-
-	types := []typeDef{
-		{
-			Name:  "int",
-			Value: "123",
-			Bytes: 32,
-		},
-		{
-			Name:  "int8",
-			Value: "123",
-			Bytes: 8,
-		},
-		{
-			Name:  "int16",
-			Value: "123",
-			Bytes: 16,
-		},
-		{
-			Name:  "int32",
-			Value: "123",
-			Bytes: 32,
-		},
-		{
-			Name:  "int64",
-			Value: "123",
-			Bytes: 64,
-		},
-	}
-
-	f, err := os.Create("context/getparam.go")
-	die(err)
-
-	var tmpl = template.Must(template.New("").Funcs(funcMap).Parse(getTmpl))
-
-	err = tmpl.Execute(f, struct {
-		IntTypes   []typeDef
-		FloatTypes []typeDef
-	}{IntTypes: types, FloatTypes: floatTypes})
-	die(err)
-	f.Close()
-
-	f, err = os.Create("context/getparam_test.go")
-	die(err)
-	tmpl = template.Must(template.New("getparams").Funcs(funcMap).Parse(testTmpl))
-	err = tmpl.Execute(f, struct {
-		IntTypes   []typeDef
-		FloatTypes []typeDef
-	}{IntTypes: types, FloatTypes: floatTypes})
-	die(err)
-	f.Close()
-}
-
-func die(err error) {
-	if err != nil {
-		log.Fatal(err)
-	}
-}
-
-var testTmpl = `// Code generated .* DO NOT EDIT.
-package context
-
-import (
-	"strconv"
-	"testing"
-	"fmt"
-)
-{{ range .FloatTypes }}
-func TestContext_Get{{ .Name | Title }}Param(t *testing.T) {
-	ctx := New()
-	given := {{ .Name }}({{ .Value | noescape }})
-	bs := float64ToByte(float64(given))
-	ctx.SetParams([][]byte{
-		bs,
-	})
-
-	received := ctx.Get{{ .Name | Title }}Param(0)
-	if received != given {
-		t.Errorf("expected %+v bug %+v received", given, received)
-	}
-}
-{{ end }}
-func float64ToByte(f float64) []byte {
-	return []byte(fmt.Sprintf("%f", f))
-}
-{{ range .IntTypes }}
-func TestContext_Get{{ .Name | Title }}Param(t *testing.T) {
-	ctx := New()
-	given := {{ .Name }}(123)
-	bs := []byte(strconv.Itoa(int(given)))
-	ctx.SetParams([][]byte{
-		bs,
-	})
-
-	received := ctx.Get{{ .Name | Title }}Param(0)
-	if received != given {
-		t.Errorf("expected %+v bug %+v received", given, received)
-	}
-}
-
-func TestContext_Get{{ .Name | Title }}Param_PanicsOnNoSuchParam(t *testing.T) {
-	ctx := New()
-	defer func() {
-        if r := recover(); r == nil {
-            t.Error("the Get{{ .Name | Title }}Param should panic")
-        }
-    }()
-	_ = ctx.Get{{ .Name | Title }}Param(0)
-}
-{{ end }}
-`
-
-var getTmpl = `// Code generated .* DO NOT EDIT.
-package context
-
-import (
-	"fmt"
-	"strconv"
-)
-
-func (ctx Context) GetBoolParam(i int) bool {
-	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %+v does not exist", i))
-	}
-
-	v, err := strconv.ParseBool(string(ctx.params[i]))
-	if err != nil {
-		panic(fmt.Sprintf("cannot convert to bool"))
-	}
-
-	return v
-}
-
-func (ctx Context) GetStringParam(i int) string {
-	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %+v does not exist", i))
-	}
-
-	return string(ctx.params[i])
-}
-
-{{ range .FloatTypes }}
-func (ctx Context) Get{{ .Name | Title }}Param(i int) {{ .Name }} {
-	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %+v does not exist", i))
-	}
-
-	data := ctx.params[i]
-	param, err := strconv.ParseFloat(string(data), {{ .Bytes }})
-
-	if err != nil {
-		panic(err)
-	}
-
-	return {{ .Name }}(param)
-}
-{{ end }}
-{{ range .IntTypes }}
-func (ctx Context) Get{{ .Name | Title }}Param(i int) {{ .Name }} {
-	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %+v does not exist", i))
-	}
-
-	data := ctx.params[i]
-	param, err := strconv.ParseInt(string(data), 10, {{ .Bytes }})
-
-	if err != nil {
-		panic(err)
-	}
-
-	return {{ .Name }}(param)
-}
-{{ end }}
-`
diff --git a/gobdd.go b/gobdd.go
index 5881045..86f9906 100644
--- a/gobdd.go
+++ b/gobdd.go
@@ -8,6 +8,7 @@ import (
 	"log"
 	"os"
 	"path/filepath"
+	"reflect"
 	"regexp"
 	"strconv"
 	"strings"
@@ -15,7 +16,6 @@ import (
 
 	"github.com/cucumber/gherkin-go"
 	"github.com/go-bdd/gobdd/context"
-	"github.com/go-bdd/gobdd/step"
 )
 
 // Holds all the information about the suite (options, steps to execute etc)
@@ -80,7 +80,7 @@ func (options SuiteOptions) WithIgnoredTags(tags []string) SuiteOptions {
 
 type stepDef struct {
 	expr *regexp.Regexp
-	f    step.Func
+	f    interface{}
 }
 
 // Creates a new suites with given configuration and empty steps defined
@@ -103,7 +103,7 @@ func NewSuite(t *testing.T, options SuiteOptions) *Suite {
 //
 // The second parameter is a function which will be executed when while running a scenario one of the steps will match
 // the given pattern
-func (s *Suite) AddStep(step interface{}, f step.Func) error {
+func (s *Suite) AddStep(step interface{}, f interface{}) error {
 	var regex *regexp.Regexp
 
 	switch t := step.(type) {
@@ -242,6 +242,7 @@ func (s *Suite) runOutlineStep(outline *gherkin.ScenarioOutline, placeholders []
 	var steps []*gherkin.Step
 	for _, outlineStep := range outline.Steps {
 		text := outlineStep.Text
+
 		for i, placeholder := range placeholders {
 			ph := "<" + placeholder.Value + ">"
 			index := strings.Index(text, ph)
@@ -253,15 +254,13 @@ func (s *Suite) runOutlineStep(outline *gherkin.ScenarioOutline, placeholders []
 			text = strings.Replace(text, "<"+placeholder.Value+">", group.Cells[i].Value, -1)
 			t := getRegexpForVar(group.Cells[i].Value)
 
-			for _, step := range s.steps {
-				def, err := s.findStepDef(originalText)
-				if err != nil {
-					continue
-				}
-
-				expr := strings.Replace(def.expr.String(), ph, t, -1)
-				_ = s.AddStep(expr, step.f)
+			def, err := s.findStepDef(originalText)
+			if err != nil {
+				continue
 			}
+
+			expr := strings.Replace(def.expr.String(), ph, t, -1)
+			_ = s.AddStep(expr, def.f)
 		}
 
 		arg := s.getOutlineArguments(outlineStep, placeholders, group)
@@ -336,8 +335,6 @@ func (s *Suite) runScenario(ctx context.Context, scenario *gherkin.Scenario, bkg
 		}
 		s.runSteps(ctx, t, scenario.Steps)
 	})
-
-
 	return nil
 }
 
@@ -360,27 +357,67 @@ func (s *Suite) runStep(ctx context.Context, t *testing.T, step *gherkin.Step) {
 		return
 	}
 
-	b := def.expr.FindSubmatch([]byte(step.Text))
-	ctx.SetParams(b[1:])
+	params := def.expr.FindSubmatch([]byte(step.Text))[1:]
 	t.Run(step.Text, func(t *testing.T) {
-		err = def.f(ctx)
-		if err != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				t.Errorf("%+v", r)
+			}
+		}()
 
+		d := reflect.ValueOf(def.f)
+		in := []reflect.Value{reflect.ValueOf(ctx)}
+		if len(params) + 1 != d.Type().NumIn() {
+			t.Errorf("the step function %s accepts %d arguments but %d received", d.String(), d.Type().NumIn(), len(params) + 1)
+			return
+		}
+		for i, v := range params {
+			inType := d.Type().In(i + 1)
+			paramType := s.paramType(v, inType)
+			in = append(in, paramType)
+		}
+		v := d.Call(in)[0]
+
+		if !v.IsNil() {
+			err = v.Interface().(error)
 			t.Errorf(step.Keyword, step.Text, err)
 		}
 	})
 }
 
+func (s *Suite) paramType(param []byte, inType reflect.Type) reflect.Value {
+	paramType := reflect.ValueOf(param)
+	if inType.Kind() == reflect.String {
+		paramType = reflect.ValueOf(string(paramType.Interface().([]uint8)))
+	}
+	if inType.Kind() == reflect.Int {
+		s := paramType.Interface().([]uint8)
+		p, _ := strconv.Atoi(string(s))
+		paramType = reflect.ValueOf(p)
+	}
+	return paramType
+}
+
 func (s *Suite) findStepDef(text string) (stepDef, error) {
+	var sd stepDef
+	found := 0
+
 	for _, step := range s.steps {
 		if !step.expr.MatchString(text) {
 			continue
 		}
 
-		return step, nil
+		if l := len(step.expr.FindAll([]byte(text), -1)); l > found {
+			found = l
+			sd = step
+		}
+	}
+
+	if reflect.DeepEqual(sd, stepDef{}) {
+		return sd, errors.New("cannot find step definition")
 	}
 
-	return stepDef{}, errors.New("cannot find step definition")
+	return sd, nil
 }
 
 func (s *Suite) skipScenario(scenarioTags []*gherkin.Tag) bool {
diff --git a/step/func.go b/step/func.go
deleted file mode 100644
index 86379e7..0000000
--- a/step/func.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package step
-
-import "github.com/go-bdd/gobdd/context"
-
-// Every step function have to be compatible with this type
-type Func func(ctx context.Context) error


Test patch:
diff --git a/context/getparam_test.go b/context/getparam_test.go
deleted file mode 100644
index e196518..0000000
--- a/context/getparam_test.go
+++ /dev/null
@@ -1,161 +0,0 @@
-// Code generated .* DO NOT EDIT.
-package context
-
-import (
-	"strconv"
-	"testing"
-	"fmt"
-)
-
-func TestContext_GetFloat32Param(t *testing.T) {
-	ctx := New()
-	given := float32(123.5)
-	bs := float64ToByte(float64(given))
-	ctx.SetParams([][]byte{
-		bs,
-	})
-
-	received := ctx.GetFloat32Param(0)
-	if received != given {
-		t.Errorf("expected %+v bug %+v received", given, received)
-	}
-}
-
-func TestContext_GetFloat64Param(t *testing.T) {
-	ctx := New()
-	given := float64(123.5)
-	bs := float64ToByte(float64(given))
-	ctx.SetParams([][]byte{
-		bs,
-	})
-
-	received := ctx.GetFloat64Param(0)
-	if received != given {
-		t.Errorf("expected %+v bug %+v received", given, received)
-	}
-}
-
-func float64ToByte(f float64) []byte {
-	return []byte(fmt.Sprintf("%f", f))
-}
-
-func TestContext_GetIntParam(t *testing.T) {
-	ctx := New()
-	given := int(123)
-	bs := []byte(strconv.Itoa(int(given)))
-	ctx.SetParams([][]byte{
-		bs,
-	})
-
-	received := ctx.GetIntParam(0)
-	if received != given {
-		t.Errorf("expected %+v bug %+v received", given, received)
-	}
-}
-
-func TestContext_GetIntParam_PanicsOnNoSuchParam(t *testing.T) {
-	ctx := New()
-	defer func() {
-        if r := recover(); r == nil {
-            t.Error("the GetIntParam should panic")
-        }
-    }()
-	_ = ctx.GetIntParam(0)
-}
-
-func TestContext_GetInt8Param(t *testing.T) {
-	ctx := New()
-	given := int8(123)
-	bs := []byte(strconv.Itoa(int(given)))
-	ctx.SetParams([][]byte{
-		bs,
-	})
-
-	received := ctx.GetInt8Param(0)
-	if received != given {
-		t.Errorf("expected %+v bug %+v received", given, received)
-	}
-}
-
-func TestContext_GetInt8Param_PanicsOnNoSuchParam(t *testing.T) {
-	ctx := New()
-	defer func() {
-        if r := recover(); r == nil {
-            t.Error("the GetInt8Param should panic")
-        }
-    }()
-	_ = ctx.GetInt8Param(0)
-}
-
-func TestContext_GetInt16Param(t *testing.T) {
-	ctx := New()
-	given := int16(123)
-	bs := []byte(strconv.Itoa(int(given)))
-	ctx.SetParams([][]byte{
-		bs,
-	})
-
-	received := ctx.GetInt16Param(0)
-	if received != given {
-		t.Errorf("expected %+v bug %+v received", given, received)
-	}
-}
-
-func TestContext_GetInt16Param_PanicsOnNoSuchParam(t *testing.T) {
-	ctx := New()
-	defer func() {
-        if r := recover(); r == nil {
-            t.Error("the GetInt16Param should panic")
-        }
-    }()
-	_ = ctx.GetInt16Param(0)
-}
-
-func TestContext_GetInt32Param(t *testing.T) {
-	ctx := New()
-	given := int32(123)
-	bs := []byte(strconv.Itoa(int(given)))
-	ctx.SetParams([][]byte{
-		bs,
-	})
-
-	received := ctx.GetInt32Param(0)
-	if received != given {
-		t.Errorf("expected %+v bug %+v received", given, received)
-	}
-}
-
-func TestContext_GetInt32Param_PanicsOnNoSuchParam(t *testing.T) {
-	ctx := New()
-	defer func() {
-        if r := recover(); r == nil {
-            t.Error("the GetInt32Param should panic")
-        }
-    }()
-	_ = ctx.GetInt32Param(0)
-}
-
-func TestContext_GetInt64Param(t *testing.T) {
-	ctx := New()
-	given := int64(123)
-	bs := []byte(strconv.Itoa(int(given)))
-	ctx.SetParams([][]byte{
-		bs,
-	})
-
-	received := ctx.GetInt64Param(0)
-	if received != given {
-		t.Errorf("expected %+v bug %+v received", given, received)
-	}
-}
-
-func TestContext_GetInt64Param_PanicsOnNoSuchParam(t *testing.T) {
-	ctx := New()
-	defer func() {
-        if r := recover(); r == nil {
-            t.Error("the GetInt64Param should panic")
-        }
-    }()
-	_ = ctx.GetInt64Param(0)
-}
-
diff --git a/gobdd_test.go b/gobdd_test.go
index f53f154..2a5f54d 100644
--- a/gobdd_test.go
+++ b/gobdd_test.go
@@ -7,31 +7,6 @@ import (
 	"github.com/go-bdd/gobdd/context"
 )
 
-func add(ctx context.Context) error {
-	res := ctx.GetIntParam(0) + ctx.GetIntParam(1)
-	ctx.Set("sumRes", res)
-	return nil
-}
-
-func check(ctx context.Context) error {
-	expected := ctx.GetIntParam(0)
-	received := ctx.GetInt("sumRes")
-
-	if expected != received {
-		return errors.New("the math does not work for you")
-	}
-
-	return nil
-}
-
-func fail(context.Context) error {
-	return errors.New("the step should never be executed")
-}
-
-func pass(context.Context) error {
-	return nil
-}
-
 func TestScenarios(t *testing.T) {
 	suite := NewSuite(t, NewSuiteOptions().WithFeaturesPath("features/example.feature"))
 	err := suite.AddStep(`I add (\d+) and (\d+)`, add)
@@ -49,17 +24,12 @@ func TestScenarios(t *testing.T) {
 
 func TestScenarioOutline(t *testing.T) {
 	suite := NewSuite(t, NewSuiteOptions().WithFeaturesPath("features/outline.feature"))
-	err := suite.AddStep(`I add <digit1> and <digit2>`, func(ctx context.Context) error {
-		return nil
-	})
+	err := suite.AddStep(`I add <digit1> and <digit2>`, add)
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	err = suite.AddStep(`the result should equal <result>`, func(ctx context.Context) error {
-
-		return nil
-	})
+	err = suite.AddStep(`the result should equal <result>`, check)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -110,3 +80,28 @@ func TestIgnoredTags(t *testing.T) {
 
 	suite.Run()
 }
+
+
+func add(ctx context.Context, var1, var2 int) error {
+	res := var1 + var2
+	ctx.Set("sumRes", res)
+	return nil
+}
+
+func check(ctx context.Context, sum int) error {
+	received := ctx.GetInt("sumRes")
+
+	if sum != received {
+		return errors.New("the math does not work for you")
+	}
+
+	return nil
+}
+
+func fail(context.Context) error {
+	return errors.New("the step should never be executed")
+}
+
+func pass(context.Context) error {
+	return nil
+}
\ No newline at end of file
diff --git a/testhttp/setup.go b/testhttp/setup.go
index 99cb7f8..50b5577 100644
--- a/testhttp/setup.go
+++ b/testhttp/setup.go
@@ -7,13 +7,12 @@ import (
 	"fmt"
 	"github.com/go-bdd/assert"
 	"github.com/go-bdd/gobdd/context"
-	"github.com/go-bdd/gobdd/step"
 	"io/ioutil"
 	"net/http"
 )
 
 type addStepper interface {
-	AddStep(step interface{}, f step.Func) error
+	AddStep(step interface{}, f interface{}) error
 }
 
 type testHTTPMethods struct {
@@ -44,44 +43,36 @@ func Build(addStep addStepper, h httpHandler) TestHTTP {
 	return thhtp
 }
 
-func (t testHTTPMethods) iSetRequestBodyTo(ctx context.Context) error {
+func (t testHTTPMethods) iSetRequestBodyTo(ctx context.Context, body string) error {
 	r := ctx.Get(RequestKey{}).(*http.Request)
-	body := ctx.GetStringParam(0)
 	r.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(body)))
 	ctx.Set(RequestKey{}, r)
 	return nil
 }
 
-func (t testHTTPMethods) iSetRequestSetTo(ctx context.Context) error {
+func (t testHTTPMethods) iSetRequestSetTo(ctx context.Context, headerName, value string) error {
 	req := ctx.Get(RequestKey{}).(*http.Request)
-	headerName := ctx.GetStringParam(0)
-	value := ctx.GetStringParam(1)
 	req.Header.Add(headerName, value)
 
 	ctx.Set(RequestKey{}, req)
 	return nil
 }
 
-func (t testHTTPMethods) responseHeaderEquals(ctx context.Context) error {
+func (t testHTTPMethods) responseHeaderEquals(ctx context.Context, headerName, expected string) error {
 	resp := ctx.Get(ResponseKey{}).(Response)
-	headerName := ctx.GetStringParam(0)
-	expected := ctx.GetStringParam(1)
 	given := resp.Header.Get(headerName)
 
 	return assert.Equals(expected, given)
 }
 
-func (t testHTTPMethods) theRequestHasBody(ctx context.Context) error {
+func (t testHTTPMethods) theRequestHasBody(ctx context.Context, body string) error {
 	req := ctx.Get(RequestKey{}).(*http.Request)
-	body := ctx.GetStringParam(0)
 	req.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(body)))
 	ctx.Set(RequestKey{}, req)
 	return nil
 }
 
-func (t testHTTPMethods) iHaveARequest(ctx context.Context) error {
-	method := ctx.GetStringParam(0)
-	url := ctx.GetStringParam(1)
+func (t testHTTPMethods) iHaveARequest(ctx context.Context, method, url string) error {
 	req, err := http.NewRequest(method, url, nil)
 	if err != nil {
 		return err
@@ -91,9 +82,7 @@ func (t testHTTPMethods) iHaveARequest(ctx context.Context) error {
 	return nil
 }
 
-func (t testHTTPMethods) theResponseIs(ctx context.Context) error {
-	expectedResponse := ctx.GetStringParam(0)
-
+func (t testHTTPMethods) theResponseIs(ctx context.Context, expectedResponse string) error {
 	resp := ctx.Get(ResponseKey{}).(Response)
 	body, err := ioutil.ReadAll(resp.Body)
 	resp.Body = ioutil.NopCloser(bytes.NewBuffer(body))
@@ -134,9 +123,7 @@ func (t testHTTPMethods) iMakeRequest(ctx context.Context) error {
 	return nil
 }
 
-func (t testHTTPMethods) makeRequest(ctx context.Context) error {
-	method := ctx.GetStringParam(0)
-	url := ctx.GetStringParam(1)
+func (t testHTTPMethods) makeRequest(ctx context.Context, method, url string) error {
 	req, err := http.NewRequest(method, url, nil)
 	if err != nil {
 		return err
@@ -151,8 +138,7 @@ func (t testHTTPMethods) makeRequest(ctx context.Context) error {
 	return nil
 }
 
-func (t testHTTPMethods) statusCodeEquals(ctx context.Context) error {
-	expectedStatus := ctx.GetIntParam(0)
+func (t testHTTPMethods) statusCodeEquals(ctx context.Context, expectedStatus int) error {
 	resp := ctx.Get(ResponseKey{}).(Response)
 
 	if expectedStatus != resp.Code {
diff --git a/testhttp_test.go b/testhttp_test.go
index df5d43f..142985e 100644
--- a/testhttp_test.go
+++ b/testhttp_test.go
@@ -40,9 +40,9 @@ func TestHTTP(t *testing.T) {
 	s.Run()
 }
 
-func requestHasMethodSetTo(ctx context.Context) error {
+func requestHasMethodSetTo(ctx context.Context, method string) error {
 	r := ctx.Get(testhttp.RequestKey{}).(*http.Request)
-	return assert.Equals(ctx.GetStringParam(0), r.Method)
+	return assert.Equals(method, r.Method)
 }
 
 func requestBodyIsNil(ctx context.Context) error {
@@ -50,22 +50,20 @@ func requestBodyIsNil(ctx context.Context) error {
 	return assert.Nil(r.Body)
 }
 
-func urlIsSetTo(ctx context.Context) error {
+func urlIsSetTo(ctx context.Context, url string) error {
 	r := ctx.Get(testhttp.RequestKey{}).(*http.Request)
-	return assert.Equals(ctx.GetStringParam(0), r.URL.String())
+	return assert.Equals(url, r.URL.String())
 }
 
-func ISetHeaderTo(ctx context.Context) error {
+func ISetHeaderTo(ctx context.Context, headerName, value string) error {
 	r := ctx.Get(testhttp.RequestKey{}).(*http.Request)
-	r.Header.Set(ctx.GetStringParam(0), ctx.GetStringParam(1))
+	r.Header.Set(headerName, value)
 	ctx.Set(testhttp.RequestKey{}, r)
 	return nil
 }
 
-func requestHasHeaderSetTo(ctx context.Context) error {
+func requestHasHeaderSetTo(ctx context.Context, headerName,expected  string) error {
 	r := ctx.Get(testhttp.RequestKey{}).(*http.Request)
-	headerName := ctx.GetStringParam(0)
-	expected := ctx.GetStringParam(1)
 	given := r.Header.Get(headerName)
 	return assert.Equals(expected, given)
 }