Repository: go-bdd/gobdd (go)

Issue:
proposal: Replace errors with logging
**Describe the solution you'd like**
Right now, every step returns the context and error.

```go
func step(ctx context.Context) (context.Context, error) {
   return ctx, errors.New("the math doesn't work for you")
}
```

My proposal is about removing the error from the step definition and replace them with logging. We can add them to the `context`

```go
func step(ctx context.Context) context.Context {
   ctx.Error("the math doesn't work for you")
   return ctx
}
```

or

```go
func step(TestingT t, ctx context.Context) context.Context {
   t.Error("the math doesn't work for you")
   return ctx
}
```

Available methods would be:

* `t.Error`
* `t.Errorf`
* `t.Log`
* `t.Logf`
* `t.Fatal`
* `t.Fatalf`

This proposal would resolve the issue in #63 . We can get read of panics and replace them with just regular function calls.

Pull request:
replace errors with logging
Fixes #73 

Todo:
 * [x] Update docs
 * [x] add more tests to improve the code coverage

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index da48ba1..e1f7bee 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -12,7 +12,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        go: ['1.12', '1.13']
+        go: ['1.12', '1.13', '1.14']
     env:
       VERBOSE: 1
       GOFLAGS: -mod=readonly
diff --git a/context/context.go b/context/context.go
index 6b801b4..8f297cd 100644
--- a/context/context.go
+++ b/context/context.go
@@ -23,7 +23,7 @@ func (ctx Context) Set(key interface{}, value interface{}) {
 
 // Returns the data under the key.
 // If couldn't find anything but the default value is provided, returns the default value.
-// Otherwise, it panics.
+// Otherwise, it returns an error.
 func (ctx Context) Get(key interface{}, defaultValue ...interface{}) (interface{}, error) {
 	if _, ok := ctx.values[key]; !ok {
 		if len(defaultValue) == 1 {
@@ -35,6 +35,7 @@ func (ctx Context) Get(key interface{}, defaultValue ...interface{}) (interface{
 	return ctx.values[key], nil
 }
 
+// It is a shortcut for getting the value already casted as error.
 func (ctx Context) GetError(key interface{}, defaultValue ...interface{}) (interface{}, error) {
 	if _, ok := ctx.values[key]; !ok {
 		if len(defaultValue) == 1 {
diff --git a/docs/creating-steps.markdown b/docs/creating-steps.markdown
index 7727d4e..e86b5e7 100644
--- a/docs/creating-steps.markdown
+++ b/docs/creating-steps.markdown
@@ -5,10 +5,10 @@ title: Creating steps
 
 # Creating steps
 
-Every step function should accept the `Context` as the first parameter and returns the `Context` and `error`. Here's an example:
+Every step function should accept the `StepTest` as the first 2 parameters and returns the `Context`. Here's an example:
 
 ```go
-type StepFunc func(ctx context.Context, var1 int, var2 string) (context.Context, error)
+type StepFunc func(gobdd.StepTest, ctx context.Context, var1 int, var2 string) context.Context
 ```
 
 What's important to stress - the context is a [custom struct](https://github.com/go-bdd/gobdd/tree/master/context), not the built-in interface.
diff --git a/docs/quick-start.markdown b/docs/quick-start.markdown
index 8ac21c3..fb46c79 100644
--- a/docs/quick-start.markdown
+++ b/docs/quick-start.markdown
@@ -12,23 +12,24 @@ go get github.com/go-bdd/gobdd
 Add a new test `main_test.go`:
 
 ```go
-func add(ctx context.Context, var1, var2 int) (context.Context, error) {
+func add(t gobdd.StepTest, ctx context.Context, var1, var2 int) context.Context{
 	res := var1 + var2
 	ctx.Set("sumRes", res)
-	return ctx, nil
+	return ctx
 }
 
-func check(ctx context.Context, sum int) (context.Context, error) {
+func check(t gobdd.StepTest, ctx context.Context, sum int) context.Context{
 	received, err := ctx.GetInt("sumRes")
     if err != nil {
-        return ctx, err
+        t.Error(err)
+        return ctx
     }
 
 	if sum != received {
-		return errors.New("the math does not work for you")
+        t.Error(errors.New("the math does not work for you"))
 	}
 
-	return ctx, nil
+	return ctx
 }
 
 func TestScenarios(t *testing.T) {
diff --git a/fmt.go b/fmt.go
deleted file mode 100644
index 6229f31..0000000
--- a/fmt.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package gobdd
-
-import (
-	"fmt"
-
-	. "github.com/logrusorgru/aurora"
-)
-
-func printErrorf(format string, params ...interface{}) {
-	fmt.Print(Red(fmt.Sprintf(format, params...)))
-}
diff --git a/gobdd.go b/gobdd.go
index 50c9a56..79a634f 100644
--- a/gobdd.go
+++ b/gobdd.go
@@ -21,10 +21,10 @@ import (
 
 // Holds all the information about the suite (options, steps to execute etc)
 type Suite struct {
-	t           TestingT
-	steps       []stepDef
-	options     SuiteOptions
-	stepsErrors []error
+	t             TestingT
+	steps         []stepDef
+	options       SuiteOptions
+	hasStepErrors bool
 }
 
 // Holds all the information about how the suite or features/steps should be configured
@@ -98,11 +98,17 @@ type stepDef struct {
 	f    interface{}
 }
 
-type TestingT interface {
+type StepTest interface {
 	Log(...interface{})
+	Logf(string, ...interface{})
 	Fatal(...interface{})
 	Fatalf(string, ...interface{})
 	Errorf(string, ...interface{})
+	Error(...interface{})
+}
+
+type TestingT interface {
+	StepTest
 	Parallel()
 	Fail()
 	Run(name string, f func(t *testing.T)) bool
@@ -128,22 +134,24 @@ func NewSuite(t TestingT, optionClosures ...func(*SuiteOptions)) *Suite {
 // when a step definition matches the provided regular expression.
 //
 // A step function can have any number of parameters (even zero),
-// but it MUST accept a context.Context as the first parameter (if there is any)
-// and MUST return a context and an error:
+// but it MUST accept a gobdd.StepTest and context.Context as the first parameters (if there is any)
+// and MUST return the context.Context back:
 //
-// 	func myStepFunction(ctx context.Context, first int, second int) (context.Context, error) {
-// 		return ctx, nil
+// 	func myStepFunction(t gobdd.StepTest, ctx context.Context, first int, second int) context.Context {
+// 		return ctx
 // 	}
 func (s *Suite) AddStep(expr string, step interface{}) {
 	err := validateStepFunc(step)
 	if err != nil {
-		s.stepsErrors = append(s.stepsErrors, err)
+		s.t.Errorf("the step function for step `%s` is incorrect: %w", expr, err)
+		s.hasStepErrors = true
 		return
 	}
 
 	compiled, err := regexp.Compile(expr)
 	if err != nil {
-		s.stepsErrors = append(s.stepsErrors, err)
+		s.t.Errorf("the step function is incorrect: %w", err)
+		s.hasStepErrors = true
 		return
 	}
 
@@ -159,16 +167,17 @@ func (s *Suite) AddStep(expr string, step interface{}) {
 // when a step definition matches the provided regular expression.
 //
 // A step function can have any number of parameters (even zero),
-// but it MUST accept a context.Context as the first parameter (if there is any)
-// and MUST return a context and an error:
+// but it MUST accept a gobdd.StepTest and context.Context as the first parameters (if there is any)
+// and MUST return the context.Context back:
 //
-// 	func myStepFunction(ctx context.Context, first int, second int) (context.Context, error) {
-// 		return ctx, nil
+// 	func myStepFunction(t gobdd.StepTest, ctx context.Context, first int, second int) context.Context {
+// 		return ctx
 // 	}
 func (s *Suite) AddRegexStep(expr *regexp.Regexp, step interface{}) {
 	err := validateStepFunc(step)
 	if err != nil {
-		s.stepsErrors = append(s.stepsErrors, err)
+		s.t.Errorf("the step function is incorrect: %w", err)
+		s.hasStepErrors = true
 		return
 	}
 
@@ -180,10 +189,7 @@ func (s *Suite) AddRegexStep(expr *regexp.Regexp, step interface{}) {
 
 // Executes the suite with given options and defined steps
 func (s *Suite) Run() {
-	if len(s.stepsErrors) > 0 {
-		for _, err := range s.stepsErrors {
-			s.t.Log(err)
-		}
+	if s.hasStepErrors {
 		s.t.Fatal("the test contains invalid step definitions")
 		return
 	}
@@ -215,7 +221,7 @@ func (s *Suite) executeFeature(file string) error {
 	doc, err := gherkin.ParseGherkinDocument(fileIO, (&messages.Incrementing{}).NewId)
 	if err != nil {
 		s.t.Fail()
-		printErrorf("error while loading document: %s\n", err)
+		s.t.Errorf("error while loading document: %s\n", err)
 		return fmt.Errorf("error while loading document: %s\n", err)
 	}
 
@@ -378,24 +384,22 @@ func (def *stepDef) run(ctx context.Context, t TestingT, step *messages.GherkinD
 	}()
 
 	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)
+	if len(params)+2 != d.Type().NumIn() {
+		t.Errorf("the step function %s accepts %d arguments but %d received", d.String(), d.Type().NumIn(), len(params)+2)
 		return ctx
 	}
+
+	in := []reflect.Value{reflect.ValueOf(t), reflect.ValueOf(ctx)}
 	for i, v := range params {
-		inType := d.Type().In(i + 1)
+		if len(params) < i+1 {
+			break
+		}
+		inType := d.Type().In(i + 2)
 		paramType := paramType(v, inType)
 		in = append(in, paramType)
 	}
-	retValues := d.Call(in)
-	v := retValues[1]
-
-	if !v.IsNil() {
-		err := v.Interface().(error)
-		t.Errorf("%s %s: %v", step.Keyword, step.Text, err)
-	}
 
+	retValues := d.Call(in)
 	return retValues[0].Interface().(context.Context)
 }
 
diff --git a/steps.go b/steps.go
index 92612b5..e327815 100644
--- a/steps.go
+++ b/steps.go
@@ -13,8 +13,8 @@ func validateStepFunc(f interface{}) error {
 		return errors.New("the parameter should be a function")
 	}
 
-	if value.Type().NumOut() != 2 {
-		return errors.New("the function should return the context.Context and error")
+	if value.Type().NumOut() != 1 {
+		return errors.New("the function should return the context.Context")
 	}
 	val := value.Type().Out(0)
 	n := val.ConvertibleTo(reflect.TypeOf((*context.Context)(nil)).Elem())
@@ -22,20 +22,20 @@ func validateStepFunc(f interface{}) error {
 		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")
+	if value.Type().NumIn() < 2 {
+		return errors.New("the function should have StepTest and Context as the first argument")
 	}
 
-	if value.Type().NumIn() < 1 {
-		return errors.New("the function should have Context as the first argument")
+	val = value.Type().In(0)
+	testingInterface := reflect.TypeOf((*StepTest)(nil)).Elem()
+	if !val.Implements(testingInterface) {
+		return errors.New("the function should have the StepTest as the first argument")
 	}
 
-	val = value.Type().In(0)
+	val = value.Type().In(1)
 	n = val.ConvertibleTo(reflect.TypeOf((*context.Context)(nil)).Elem())
 	if !n {
-		return errors.New("the function should have Context as the first argument")
+		return errors.New("the function should have Context as the second argument")
 	}
 	return nil
 }


Test patch:
diff --git a/gobdd_test.go b/gobdd_test.go
index 4500887..557ceb5 100644
--- a/gobdd_test.go
+++ b/gobdd_test.go
@@ -3,6 +3,7 @@ package gobdd
 import (
 	"errors"
 	"fmt"
+	"regexp"
 	"testing"
 
 	"github.com/cucumber/messages-go/v9"
@@ -11,6 +12,16 @@ import (
 )
 
 func TestScenarios(t *testing.T) {
+	suite := NewSuite(t, WithFeaturesPath("features/example.feature"))
+	compiled, _ := regexp.Compile(`I add (\d+) and (\d+)`)
+	suite.AddRegexStep(compiled, add)
+	compiled, _ = regexp.Compile(`the result should equal (\d+)`)
+	suite.AddRegexStep(compiled, check)
+
+	suite.Run()
+}
+
+func TestAddStepWithRegexp(t *testing.T) {
 	suite := NewSuite(t, WithFeaturesPath("features/example.feature"))
 	suite.AddStep(`I add (\d+) and (\d+)`, add)
 	suite.AddStep(`the result should equal (\d+)`, check)
@@ -88,7 +99,7 @@ func TestFailureOutput(t *testing.T) {
 		expectedErrors []string
 	}{
 		{name: "passes", f: pass, expectedErrors: nil},
-		{name: "returns error", f: failure, expectedErrors: []string{"Test text: the step failed"}},
+		{name: "returns error", f: failure, expectedErrors: []string{"the step failed"}},
 		{name: "step panics", f: panics, expectedErrors: []string{"the step panicked"}},
 	}
 
@@ -107,58 +118,63 @@ func TestFailureOutput(t *testing.T) {
 	}
 }
 
-func addf(ctx context.Context, var1, var2 float32) (context.Context, error) {
+func addf(t StepTest, ctx context.Context, var1, var2 float32) context.Context {
 	res := var1 + var2
 	ctx.Set("sumRes", res)
-	return ctx, nil
+	return ctx
 }
 
-func add(ctx context.Context, var1, var2 int) (context.Context, error) {
+func add(t StepTest, ctx context.Context, var1, var2 int) context.Context {
 	res := var1 + var2
 	ctx.Set("sumRes", res)
-	return ctx, nil
+	return ctx
 }
 
-func checkf(ctx context.Context, sum float32) (context.Context, error) {
+func checkf(t StepTest, ctx context.Context, sum float32) context.Context {
 	received, err := ctx.Get("sumRes")
 	if err != nil {
-		return ctx, err
+		t.Error(err.Error())
+		return ctx
 	}
 
 	if sum != received {
-		return ctx, errors.New("the math does not work for you")
+		t.Error("the sum doesn't match")
 	}
 
-	return ctx, nil
+	return ctx
 }
 
-func check(ctx context.Context, sum int) (context.Context, error) {
+func check(t StepTest, ctx context.Context, sum int) context.Context {
 	received, err := ctx.Get("sumRes")
 	if err != nil {
-		return ctx, err
+		t.Error(err.Error())
+		return ctx
 	}
 
 	if sum != received {
-		return ctx, errors.New("the math does not work for you")
+		t.Error("the math does not work for you")
+		return ctx
 	}
 
-	return ctx, nil
+	return ctx
 }
 
-func fail(ctx context.Context) (context.Context, error) {
-	return ctx, errors.New("the step should never be executed")
+func fail(t StepTest, ctx context.Context) context.Context {
+	t.Error("the step should never be executed")
+	return ctx
 }
 
-func failure(ctx context.Context) (context.Context, error) {
-	return ctx, errors.New("the step failed")
+func failure(t StepTest, ctx context.Context) context.Context {
+	t.Error("the step failed")
+	return ctx
 }
 
-func panics(_ context.Context) (context.Context, error) {
+func panics(t StepTest, _ context.Context) context.Context {
 	panic(errors.New("the step panicked"))
 }
 
-func pass(ctx context.Context) (context.Context, error) {
-	return ctx, nil
+func pass(t StepTest, ctx context.Context) context.Context {
+	return ctx
 }
 
 type mockTester struct {
@@ -169,6 +185,8 @@ type mockTester struct {
 func (m *mockTester) Log(...interface{}) {
 }
 
+func (m *mockTester) Logf(string, ...interface{}) {
+}
 func (m *mockTester) Fatal(...interface{}) {
 	m.fatalCalled++
 }
@@ -176,6 +194,10 @@ func (m *mockTester) Fatal(...interface{}) {
 func (m *mockTester) Fatalf(string, ...interface{}) {
 }
 
+func (m *mockTester) Error(a ...interface{}) {
+	m.errors = append(m.errors, fmt.Sprintf("%s", a...))
+}
+
 func (m *mockTester) Errorf(format string, a ...interface{}) {
 	m.errors = append(m.errors, fmt.Sprintf(format, a...))
 }
diff --git a/steps_test.go b/steps_test.go
index cdfb404..40ccd18 100644
--- a/steps_test.go
+++ b/steps_test.go
@@ -8,11 +8,11 @@ import (
 
 func TestValidateStepFunc(t *testing.T) {
 	testCases := map[string]interface{}{
-		"function without arguments":                  func() (context.Context, error) { return context.Context{}, nil },
-		"function with invalid first arguments":       func(int) (context.Context, error) { return context.Context{}, 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 context.Context{}, 0 },
+		"function without arguments":                 func() context.Context { return context.Context{} },
+		"function with 1 argument":                   func(StepTest) context.Context { return context.Context{} },
+		"function with invalid first argument":       func(int, context.Context) context.Context { return context.Context{} },
+		"function without returned values":           func(StepTest, context.Context) {},
+		"function with invalid first returned value": func(StepTest, context.Context) int { return 0 },
 	}
 
 	for name, testCase := range testCases {
@@ -25,7 +25,7 @@ func TestValidateStepFunc(t *testing.T) {
 }
 
 func TestValidateStepFunc_ValidFunction(t *testing.T) {
-	if err := validateStepFunc(func(context.Context) (context.Context, error) { return context.Context{}, nil }); err != nil {
+	if err := validateStepFunc(func(StepTest, context.Context) context.Context { return context.Context{} }); err != nil {
 		t.Errorf("the test should NOT fail for the function: %s", err)
 	}
 }
diff --git a/testhttp/setup.go b/testhttp/setup.go
index 6180d32..3ef80e5 100644
--- a/testhttp/setup.go
+++ b/testhttp/setup.go
@@ -12,6 +12,15 @@ import (
 	"github.com/go-bdd/gobdd/context"
 )
 
+type StepTest interface {
+	Log(...interface{})
+	Logf(string, ...interface{})
+	Fatal(...interface{})
+	Fatalf(string, ...interface{})
+	Errorf(string, ...interface{})
+	Error(...interface{})
+}
+
 type addStepper interface {
 	AddStep(definition string, step interface{})
 }
@@ -44,132 +53,150 @@ func Build(addStep addStepper, h httpHandler) TestHTTP {
 	return thhtp
 }
 
-func (t testHTTPMethods) iSetRequestBodyTo(ctx context.Context, body string) (context.Context, error) {
+func (th testHTTPMethods) iSetRequestBodyTo(t StepTest, ctx context.Context, body string) context.Context {
 	r, err := GetRequest(ctx)
 	if err != nil {
-		return ctx, err
+		t.Error(err)
+		return ctx
 	}
 	r.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(body)))
 	ctx.Set(RequestKey{}, r)
-	return ctx, nil
+	return ctx
 }
 
-func (t testHTTPMethods) iSetRequestSetTo(ctx context.Context, headerName, value string) (context.Context, error) {
+func (th testHTTPMethods) iSetRequestSetTo(t StepTest, ctx context.Context, headerName, value string) context.Context {
 	req, err := GetRequest(ctx)
 	if err != nil {
-		return ctx, err
+		t.Error(err)
+		return ctx
 	}
 	req.Header.Add(headerName, value)
 
 	ctx.Set(RequestKey{}, req)
-	return ctx, nil
+	return ctx
 }
 
-func (t testHTTPMethods) responseHeaderEquals(ctx context.Context, headerName, expected string) (context.Context, error) {
+func (th testHTTPMethods) responseHeaderEquals(t StepTest, ctx context.Context, headerName, expected string) context.Context {
 	resp, err := GetResponse(ctx)
 	if err != nil {
-		return ctx, err
+		t.Error(err)
+		return ctx
 	}
 	given := resp.Header.Get(headerName)
 
-	return ctx, assert.Equals(expected, given)
+	if err := assert.Equals(expected, given); err != nil {
+		t.Error(err)
+	}
+	return ctx
 }
 
-func (t testHTTPMethods) theRequestHasBody(ctx context.Context, body string) (context.Context, error) {
+func (th testHTTPMethods) theRequestHasBody(t StepTest, ctx context.Context, body string) context.Context {
 	req, err := GetRequest(ctx)
 	if err != nil {
-		return ctx, err
+		t.Error(err)
+		return ctx
 	}
 	req.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(body)))
 	ctx.Set(RequestKey{}, req)
-	return ctx, nil
+	return ctx
 }
 
-func (t testHTTPMethods) iHaveARequest(ctx context.Context, method, url string) (context.Context, error) {
+func (th testHTTPMethods) iHaveARequest(t StepTest, ctx context.Context, method, url string) context.Context {
 	req, err := http.NewRequest(method, url, nil)
 	if err != nil {
-		return ctx, err
+		t.Error(err)
+		return ctx
 	}
 
 	ctx.Set(RequestKey{}, req)
-	return ctx, nil
+	return ctx
 }
 
-func (t testHTTPMethods) theResponseIs(ctx context.Context, expectedResponse string) (context.Context, error) {
+func (th testHTTPMethods) theResponseIs(t StepTest, ctx context.Context, expectedResponse string) context.Context {
 	resp, err := GetResponse(ctx)
 	if err != nil {
-		return ctx, err
+		t.Error(err)
+		return ctx
 	}
 	body, err := ioutil.ReadAll(resp.Body)
 	resp.Body = ioutil.NopCloser(bytes.NewBuffer(body))
 	ctx.Set(ResponseKey{}, resp)
 	if err != nil {
-		return ctx, fmt.Errorf("an error while reading the body: %s", err)
+		t.Error(fmt.Errorf("an error while reading the body: %s", err))
+		return ctx
 	}
 
 	if expectedResponse != string(body) {
-		return ctx, errors.New("the body doesn't contain a valid JSON")
+		t.Error(errors.New("the body doesn't contain a valid JSON"))
 	}
-	return ctx, nil
+	return ctx
 }
 
-func (t testHTTPMethods) validJSON(ctx context.Context) (context.Context, error) {
+func (th testHTTPMethods) validJSON(t StepTest, ctx context.Context) context.Context {
 	resp, err := GetResponse(ctx)
 	if err != nil {
-		return ctx, err
+		t.Error(err)
+		return ctx
 	}
 	body, err := ioutil.ReadAll(resp.Body)
 	resp.Body = ioutil.NopCloser(bytes.NewBuffer(body))
 	ctx.Set(ResponseKey{}, resp)
 	if err != nil {
-		return ctx, fmt.Errorf("an error while reading the body: %s", err)
+		t.Error(fmt.Errorf("an error while reading the body: %s", err))
+		return ctx
 	}
 
 	if !json.Valid(body) {
-		return ctx, errors.New("the body doesn't contain a valid JSON")
+		t.Error(errors.New("the body doesn't contain a valid JSON"))
 	}
-	return ctx, nil
+	return ctx
 }
 
-func (t testHTTPMethods) iMakeRequest(ctx context.Context) (context.Context, error) {
+func (th testHTTPMethods) iMakeRequest(t StepTest, ctx context.Context) context.Context {
 	req, err := GetRequest(ctx)
 	if err != nil {
-		return ctx, err
+		t.Error(err)
+		return ctx
 	}
-	resp, err := t.tHTTP.MakeRequest(req)
+	resp, err := th.tHTTP.MakeRequest(req)
 	if err != nil {
-		return ctx, err
+		t.Error(err)
+		return ctx
 	}
 
 	ctx.Set(ResponseKey{}, resp)
-	return ctx, nil
+	return ctx
 }
 
-func (t testHTTPMethods) makeRequest(ctx context.Context, method, url string) (context.Context, error) {
+func (th testHTTPMethods) makeRequest(t StepTest, ctx context.Context, method, url string) context.Context {
 	req, err := http.NewRequest(method, url, nil)
 	if err != nil {
-		return ctx, err
+		t.Error(err)
+		return ctx
 	}
 
-	resp, err := t.tHTTP.MakeRequest(req)
+	resp, err := th.tHTTP.MakeRequest(req)
 	if err != nil {
-		return ctx, err
+		t.Error(err)
+		return ctx
 	}
 
 	ctx.Set(ResponseKey{}, resp)
-	return ctx, nil
+	return ctx
 }
 
-func (t testHTTPMethods) statusCodeEquals(ctx context.Context, expectedStatus int) (context.Context, error) {
+func (th testHTTPMethods) statusCodeEquals(t StepTest, ctx context.Context, expectedStatus int) context.Context {
 	resp, err := GetResponse(ctx)
 	if err != nil {
-		return ctx, err
+		t.Error(err)
+		return ctx
 	}
 
 	if expectedStatus != resp.Code {
-		return ctx, fmt.Errorf("expected status code: %d but %d given", expectedStatus, resp.Code)
+		t.Error(fmt.Errorf("expected status code: %d but %d given", expectedStatus, resp.Code))
+		return ctx
 	}
-	return ctx, nil
+	return ctx
 }
 
 func GetResponse(ctx context.Context) (Response, error) {
diff --git a/testhttp_test.go b/testhttp_test.go
index 12c0a23..542bbc1 100644
--- a/testhttp_test.go
+++ b/testhttp_test.go
@@ -40,45 +40,64 @@ func TestHTTP(t *testing.T) {
 	s.Run()
 }
 
-func requestHasMethodSetTo(ctx context.Context, method string) (context.Context, error) {
+func requestHasMethodSetTo(t StepTest, ctx context.Context, method string) context.Context {
 	r, err := testhttp.GetRequest(ctx)
 	if err != nil {
-		return ctx, err
+		t.Error(err)
+		return ctx
 	}
-	return ctx, assert.Equals(method, r.Method)
+
+	if err = assert.Equals(method, r.Method); err != nil {
+		t.Error(err)
+	}
+	return ctx
 }
 
-func requestBodyIsNil(ctx context.Context) (context.Context, error) {
+func requestBodyIsNil(t StepTest, ctx context.Context) context.Context {
 	r, err := testhttp.GetRequest(ctx)
 	if err != nil {
-		return ctx, err
+		t.Error(err)
+		return ctx
+	}
+	if err = assert.Nil(r.Body); err != nil {
+		t.Error(err)
 	}
-	return ctx, assert.Nil(r.Body)
+	return ctx
 }
 
-func urlIsSetTo(ctx context.Context, url string) (context.Context, error) {
+func urlIsSetTo(t StepTest, ctx context.Context, url string) context.Context {
 	r, err := testhttp.GetRequest(ctx)
 	if err != nil {
-		return ctx, err
+		t.Error(err)
+		return ctx
 	}
-	return ctx, assert.Equals(url, r.URL.String())
+
+	if err := assert.Equals(url, r.URL.String()); err != nil {
+		t.Error(err)
+	}
+	return ctx
 }
 
-func ISetHeaderTo(ctx context.Context, headerName, value string) (context.Context, error) {
+func ISetHeaderTo(t StepTest, ctx context.Context, headerName, value string) context.Context {
 	r, err := testhttp.GetRequest(ctx)
 	if err != nil {
-		return ctx, err
+		t.Error(err)
+		return ctx
 	}
 	r.Header.Set(headerName, value)
 	ctx.Set(testhttp.RequestKey{}, r)
-	return ctx, nil
+	return ctx
 }
 
-func requestHasHeaderSetTo(ctx context.Context, headerName, expected string) (context.Context, error) {
+func requestHasHeaderSetTo(t StepTest, ctx context.Context, headerName, expected string) context.Context {
 	r, err := testhttp.GetRequest(ctx)
 	if err != nil {
-		return ctx, err
+		t.Error(err)
+		return ctx
 	}
 	given := r.Header.Get(headerName)
-	return ctx, assert.Equals(expected, given)
+	if err := assert.Equals(expected, given); err != nil {
+		t.Error(err)
+	}
+	return ctx
 }