Repository: go-bdd/gobdd (go)

Issue:
Add getting/setting HTTP headers in test
**Is your feature request related to a problem? Please describe.**
While testing HTTP handlers, it's useful to set and fetch headers from requests and responses.

**Describe the solution you'd like**

My proposition is:

- `I set the header "XYZ" to "ZYX"`
- `the header "XYZ" equals to "ZYX"`


Pull request:
Add getting fetching request header
Fixes #33 

Patch:
diff --git a/context/context.go b/context/context.go
index c67bf77..761b014 100644
--- a/context/context.go
+++ b/context/context.go
@@ -37,7 +37,7 @@ func (ctx Context) Get(key interface{}, defaultValue ...interface{}) interface{}
 		if len(defaultValue) == 1 {
 			return defaultValue[0]
 		}
-		panic(fmt.Sprintf("the key %s does not exist", key))
+		panic(fmt.Sprintf("the key %+v does not exist", key))
 	}
 
 	return ctx.values[key]
diff --git a/context/get.go b/context/get.go
index c74dad2..a96f651 100644
--- a/context/get.go
+++ b/context/get.go
@@ -11,7 +11,7 @@ func (ctx Context) GetString(key interface{}, defaultValue ...string) string {
 		if len(defaultValue) == 1 {
 			return defaultValue[0]
 		}
-		panic(fmt.Sprintf("the key %s does not exist", key))
+		panic(fmt.Sprintf("the key %+v does not exist", key))
 	}
 
 	value, ok := ctx.values[key].(string)
@@ -30,7 +30,7 @@ func (ctx Context) GetInt(key interface{}, defaultValue ...int) int {
 		if len(defaultValue) == 1 {
 			return defaultValue[0]
 		}
-		panic(fmt.Sprintf("the key %s does not exist", key))
+		panic(fmt.Sprintf("the key %+v does not exist", key))
 	}
 
 	value, ok := ctx.values[key].(int)
@@ -49,7 +49,7 @@ func (ctx Context) GetInt8(key interface{}, defaultValue ...int8) int8 {
 		if len(defaultValue) == 1 {
 			return defaultValue[0]
 		}
-		panic(fmt.Sprintf("the key %s does not exist", key))
+		panic(fmt.Sprintf("the key %+v does not exist", key))
 	}
 
 	value, ok := ctx.values[key].(int8)
@@ -68,7 +68,7 @@ func (ctx Context) GetInt16(key interface{}, defaultValue ...int16) int16 {
 		if len(defaultValue) == 1 {
 			return defaultValue[0]
 		}
-		panic(fmt.Sprintf("the key %s does not exist", key))
+		panic(fmt.Sprintf("the key %+v does not exist", key))
 	}
 
 	value, ok := ctx.values[key].(int16)
@@ -87,7 +87,7 @@ func (ctx Context) GetInt32(key interface{}, defaultValue ...int32) int32 {
 		if len(defaultValue) == 1 {
 			return defaultValue[0]
 		}
-		panic(fmt.Sprintf("the key %s does not exist", key))
+		panic(fmt.Sprintf("the key %+v does not exist", key))
 	}
 
 	value, ok := ctx.values[key].(int32)
@@ -106,7 +106,7 @@ func (ctx Context) GetInt64(key interface{}, defaultValue ...int64) int64 {
 		if len(defaultValue) == 1 {
 			return defaultValue[0]
 		}
-		panic(fmt.Sprintf("the key %s does not exist", key))
+		panic(fmt.Sprintf("the key %+v does not exist", key))
 	}
 
 	value, ok := ctx.values[key].(int64)
@@ -125,7 +125,7 @@ func (ctx Context) GetFloat32(key interface{}, defaultValue ...float32) float32
 		if len(defaultValue) == 1 {
 			return defaultValue[0]
 		}
-		panic(fmt.Sprintf("the key %s does not exist", key))
+		panic(fmt.Sprintf("the key %+v does not exist", key))
 	}
 
 	value, ok := ctx.values[key].(float32)
@@ -144,7 +144,7 @@ func (ctx Context) GetFloat64(key interface{}, defaultValue ...float64) float64
 		if len(defaultValue) == 1 {
 			return defaultValue[0]
 		}
-		panic(fmt.Sprintf("the key %s does not exist", key))
+		panic(fmt.Sprintf("the key %+v does not exist", key))
 	}
 
 	value, ok := ctx.values[key].(float64)
@@ -163,7 +163,7 @@ func (ctx Context) GetBool(key interface{}, defaultValue ...bool) bool {
 		if len(defaultValue) == 1 {
 			return defaultValue[0]
 		}
-		panic(fmt.Sprintf("the key %s does not exist", key))
+		panic(fmt.Sprintf("the key %+v does not exist", key))
 	}
 
 	value, ok := ctx.values[key].(bool)
diff --git a/context/getparam.go b/context/getparam.go
index 99c12e9..8902343 100644
--- a/context/getparam.go
+++ b/context/getparam.go
@@ -8,7 +8,7 @@ import (
 
 func (ctx Context) GetBoolParam(i int) bool {
 	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %d does not exist", i))
+		panic(fmt.Sprintf("the param with index %+v does not exist", i))
 	}
 
 	v, err := strconv.ParseBool(string(ctx.params[i]))
@@ -21,7 +21,7 @@ func (ctx Context) GetBoolParam(i int) bool {
 
 func (ctx Context) GetStringParam(i int) string {
 	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %d does not exist", i))
+		panic(fmt.Sprintf("the param with index %+v does not exist", i))
 	}
 
 	return string(ctx.params[i])
@@ -30,7 +30,7 @@ func (ctx Context) GetStringParam(i int) string {
 
 func (ctx Context) GetFloat32Param(i int) float32 {
 	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %d does not exist", i))
+		panic(fmt.Sprintf("the param with index %+v does not exist", i))
 	}
 
 	data := ctx.params[i]
@@ -45,7 +45,7 @@ func (ctx Context) GetFloat32Param(i int) float32 {
 
 func (ctx Context) GetFloat64Param(i int) float64 {
 	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %d does not exist", i))
+		panic(fmt.Sprintf("the param with index %+v does not exist", i))
 	}
 
 	data := ctx.params[i]
@@ -61,7 +61,7 @@ func (ctx Context) GetFloat64Param(i int) float64 {
 
 func (ctx Context) GetIntParam(i int) int {
 	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %d does not exist", i))
+		panic(fmt.Sprintf("the param with index %+v does not exist", i))
 	}
 
 	data := ctx.params[i]
@@ -76,7 +76,7 @@ func (ctx Context) GetIntParam(i int) int {
 
 func (ctx Context) GetInt8Param(i int) int8 {
 	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %d does not exist", i))
+		panic(fmt.Sprintf("the param with index %+v does not exist", i))
 	}
 
 	data := ctx.params[i]
@@ -91,7 +91,7 @@ func (ctx Context) GetInt8Param(i int) int8 {
 
 func (ctx Context) GetInt16Param(i int) int16 {
 	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %d does not exist", i))
+		panic(fmt.Sprintf("the param with index %+v does not exist", i))
 	}
 
 	data := ctx.params[i]
@@ -106,7 +106,7 @@ func (ctx Context) GetInt16Param(i int) int16 {
 
 func (ctx Context) GetInt32Param(i int) int32 {
 	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %d does not exist", i))
+		panic(fmt.Sprintf("the param with index %+v does not exist", i))
 	}
 
 	data := ctx.params[i]
@@ -121,7 +121,7 @@ func (ctx Context) GetInt32Param(i int) int32 {
 
 func (ctx Context) GetInt64Param(i int) int64 {
 	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %d does not exist", i))
+		panic(fmt.Sprintf("the param with index %+v does not exist", i))
 	}
 
 	data := ctx.params[i]
diff --git a/features/http.feature b/features/http.feature
index 5fc74b6..fa500f8 100644
--- a/features/http.feature
+++ b/features/http.feature
@@ -9,3 +9,24 @@ Feature: HTTP requests
     When I make a GET request to "/json"
     Then the response contains a valid JSON
     And the response is "{"valid": "json"}"
+
+  Scenario: the simplest request
+    When I have a GET request "http://google.com"
+    Then the request has method set to GET
+    And the url is set to "http://google.com"
+    And the request body is nil
+  Scenario: passing headers
+    Given I have a GET request "http://google.com"
+    When I set the header "XYZ" to "ZYX"
+    Then the request has header "XYZ" set to "ZYX"
+
+  Scenario: handling response headers
+    Given I have a GET request "/mirror"
+    And I set request header "Xyz" to "ZZZ"
+    When I make the request
+    Then the response header "Xyz" equals "ZZZ"
+
+  Scenario: request should have empty body by default
+    Given I have a POST request "/mirror"
+    When I set request body to "LALA"
+    Then the request has body "LALA"
\ No newline at end of file
diff --git a/gen/ctxget.go b/gen/ctxget.go
index 74a4656..468899d 100644
--- a/gen/ctxget.go
+++ b/gen/ctxget.go
@@ -149,7 +149,7 @@ func (ctx Context) Get{{ .Name | Title }}(key interface{}, defaultValue ...{{ .N
 		if len(defaultValue) == 1 {
 			return defaultValue[0]
 		}
-		panic(fmt.Sprintf("the key %s does not exist", key))
+		panic(fmt.Sprintf("the key %+v does not exist", key))
 	}
 
 	value, ok := ctx.values[key].({{ .Name }})
diff --git a/gen/ctxgetparam.go b/gen/ctxgetparam.go
index 6f82690..8bb3b31 100644
--- a/gen/ctxgetparam.go
+++ b/gen/ctxgetparam.go
@@ -158,7 +158,7 @@ import (
 
 func (ctx Context) GetBoolParam(i int) bool {
 	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %d does not exist", i))
+		panic(fmt.Sprintf("the param with index %+v does not exist", i))
 	}
 
 	v, err := strconv.ParseBool(string(ctx.params[i]))
@@ -171,7 +171,7 @@ func (ctx Context) GetBoolParam(i int) bool {
 
 func (ctx Context) GetStringParam(i int) string {
 	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %d does not exist", i))
+		panic(fmt.Sprintf("the param with index %+v does not exist", i))
 	}
 
 	return string(ctx.params[i])
@@ -180,7 +180,7 @@ func (ctx Context) GetStringParam(i int) string {
 {{ range .FloatTypes }}
 func (ctx Context) Get{{ .Name | Title }}Param(i int) {{ .Name }} {
 	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %d does not exist", i))
+		panic(fmt.Sprintf("the param with index %+v does not exist", i))
 	}
 
 	data := ctx.params[i]
@@ -196,7 +196,7 @@ func (ctx Context) Get{{ .Name | Title }}Param(i int) {{ .Name }} {
 {{ range .IntTypes }}
 func (ctx Context) Get{{ .Name | Title }}Param(i int) {{ .Name }} {
 	if i >= len(ctx.params) {
-		panic(fmt.Sprintf("the param with index %d does not exist", i))
+		panic(fmt.Sprintf("the param with index %+v does not exist", i))
 	}
 
 	data := ctx.params[i]
diff --git a/go.mod b/go.mod
index 21491c2..7379d0b 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,7 @@ go 1.12
 
 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 637ff64..c1fb69b 100644
--- a/go.sum
+++ b/go.sum
@@ -2,6 +2,8 @@ github.com/cucumber/gherkin-go v5.1.0+incompatible h1:RCvyVI6KQLI2IJkijZBeJcE4K3
 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=
diff --git a/reporter/fmt.go b/reporter/fmt.go
index bec3a44..9b0d197 100644
--- a/reporter/fmt.go
+++ b/reporter/fmt.go
@@ -11,7 +11,7 @@ type fmtReporter struct {
 }
 
 func (r fmtReporter) Step(step *gherkin.Step) {
-	fmt.Printf("   %s %s\n", Yellow(step.Keyword), Green(step.Text))
+	fmt.Printf("   %s%s\n", Yellow(step.Keyword), Green(step.Text))
 }
 
 func (r fmtReporter) Scenario(scenario *gherkin.Scenario) {
@@ -20,7 +20,7 @@ func (r fmtReporter) Scenario(scenario *gherkin.Scenario) {
 			fmt.Printf(" %s\n", Yellow(tag.Name))
 		}
 	}
-	fmt.Printf(" %s: %s\n", Yellow(scenario.Keyword), Green(scenario.Name))
+	fmt.Printf(" %s:%s\n", Yellow(scenario.Keyword), Green(scenario.Name))
 }
 
 func (r fmtReporter) ScenarioOutline(scenario *gherkin.ScenarioOutline) {
@@ -29,38 +29,38 @@ func (r fmtReporter) ScenarioOutline(scenario *gherkin.ScenarioOutline) {
 			fmt.Printf(" %s\n", Yellow(tag.Name))
 		}
 	}
-	fmt.Printf(" %s: %s\n", Yellow(scenario.Keyword), Green(scenario.Name))
+	fmt.Printf(" %s:%s\n", Yellow(scenario.Keyword), Green(scenario.Name))
 
 	for _, example := range scenario.Examples {
 		fmt.Printf(" %s:\n", Yellow(example.Keyword))
 	}
 
 	for _, step := range scenario.Steps {
-		fmt.Printf("     %s %s\n", Yellow(step.Keyword), Green(step.Text))
+		fmt.Printf("     %s%s\n", Yellow(step.Keyword), Green(step.Text))
 	}
 }
 
 func (r fmtReporter) Undefined(s *gherkin.Step) {
-	fmt.Printf("   %s %s\n", Red("Undefined step"), Yellow(s.Text))
+	fmt.Printf("   %s%s\n", Red("Undefined step"), Yellow(s.Text))
 }
 
 func (r fmtReporter) Skip(s *gherkin.Step) {
-	fmt.Printf(" %s %s\n", s.Keyword, Gray(10, s.Text))
+	fmt.Printf(" %s%s\n", s.Keyword, Gray(10, s.Text))
 }
 
 func (r fmtReporter) Failed(step *gherkin.Step, e error) {
-	fmt.Printf("   %s %s\n", step.Keyword, Red(step.Text))
+	fmt.Printf("   %s%s\n", Yellow(step.Keyword), Red(step.Text))
 }
 
 func (r fmtReporter) Background(background *gherkin.Background) {
-	fmt.Printf("  %s %s\n", Yellow(background.Keyword), Green(background.Name))
+	fmt.Printf("  %s%s\n", Yellow(background.Keyword), Green(background.Name))
 	for _, s := range background.Steps {
 		r.Step(s)
 	}
 }
 
 func (r fmtReporter) Feature(feature *gherkin.Feature) {
-	fmt.Printf("%s %s\n", Yellow(feature.Keyword), Green(feature.Name))
+	fmt.Printf("%s%s\n", Yellow(feature.Keyword), Green(feature.Name))
 }
 
 func (r fmtReporter) SkipScenario(s *gherkin.Scenario) {
@@ -69,7 +69,7 @@ func (r fmtReporter) SkipScenario(s *gherkin.Scenario) {
 			fmt.Printf(" %s\n", Yellow(tag.Name))
 		}
 	}
-	fmt.Printf(" %s: %s\n", Gray(10, s.Keyword), Gray(10, s.Name))
+	fmt.Printf(" %s%s\n", Gray(10, s.Keyword), Gray(10, s.Name))
 	for _, step := range s.Steps {
 		r.Skip(step)
 	}
@@ -85,7 +85,7 @@ func (r fmtReporter) SkipScenarioOutline(s *gherkin.ScenarioOutline) {
 		fmt.Printf(" %s:\n", Gray(10, example.Keyword))
 	}
 
-	fmt.Printf(" %s: %s\n", Gray(10, s.Keyword), Gray(10, s.Name))
+	fmt.Printf(" %s:%s\n", Gray(10, s.Keyword), Gray(10, s.Name))
 	for _, step := range s.Steps {
 		r.Skip(step)
 	}


Test patch:
diff --git a/testhttp/requests_test.go b/testhttp/requests_test.go
index f5c6c6d..036eadd 100644
--- a/testhttp/requests_test.go
+++ b/testhttp/requests_test.go
@@ -30,5 +30,10 @@ type testHandler struct {
 }
 
 func (h testHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	for key, values := range r.Header {
+		for _, value := range values {
+			w.Header().Add(key, value)
+		}
+	}
 	_, _ = w.Write(h.body)
 }
diff --git a/testhttp/setup.go b/testhttp/setup.go
index de1b00e..99cb7f8 100644
--- a/testhttp/setup.go
+++ b/testhttp/setup.go
@@ -5,11 +5,11 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"io/ioutil"
-	"net/http"
-
+	"github.com/go-bdd/assert"
 	"github.com/go-bdd/gobdd/context"
 	"github.com/go-bdd/gobdd/step"
+	"io/ioutil"
+	"net/http"
 )
 
 type addStepper interface {
@@ -20,7 +20,8 @@ type testHTTPMethods struct {
 	tHTTP TestHTTP
 }
 
-type httpResponse struct{}
+type ResponseKey struct{}
+type RequestKey struct{}
 
 func Build(addStep addStepper, h httpHandler) TestHTTP {
 	thhtp := TestHTTP{
@@ -33,17 +34,70 @@ func Build(addStep addStepper, h httpHandler) TestHTTP {
 	_ = addStep.AddStep(`^the response code equals (\d+)$`, testHTTP.statusCodeEquals)
 	_ = addStep.AddStep(`^the response contains a valid JSON$`, testHTTP.validJSON)
 	_ = addStep.AddStep(`^the response is "(.*)"$`, testHTTP.theResponseIs)
+	_ = addStep.AddStep(`^the response header "(.*)" equals "(.*)"$`, testHTTP.responseHeaderEquals)
+	_ = addStep.AddStep(`^I have a (GET|POST|PUT|DELETE|OPTIONS) request "(.*)"$`, testHTTP.iHaveARequest)
+	_ = addStep.AddStep(`^I set request header "(.*)" to "(.*)"$`, testHTTP.iSetRequestSetTo)
+	_ = addStep.AddStep(`^I set request body to "([^"]*)"$`, testHTTP.iSetRequestBodyTo)
+	_ = addStep.AddStep(`^the request has body "(.*)"$`, testHTTP.theRequestHasBody)
+	_ = addStep.AddStep(`^I make the request$`, testHTTP.iMakeRequest)
 
 	return thhtp
 }
 
+func (t testHTTPMethods) iSetRequestBodyTo(ctx context.Context) 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 {
+	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 {
+	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 {
+	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)
+	req, err := http.NewRequest(method, url, nil)
+	if err != nil {
+		return err
+	}
+
+	ctx.Set(RequestKey{}, req)
+	return nil
+}
+
 func (t testHTTPMethods) theResponseIs(ctx context.Context) error {
 	expectedResponse := ctx.GetStringParam(0)
 
-	resp := ctx.Get(httpResponse{}).(Response)
+	resp := ctx.Get(ResponseKey{}).(Response)
 	body, err := ioutil.ReadAll(resp.Body)
 	resp.Body = ioutil.NopCloser(bytes.NewBuffer(body))
-	ctx.Set(httpResponse{}, resp)
+	ctx.Set(ResponseKey{}, resp)
 	if err != nil {
 		return fmt.Errorf("an error while reading the body: %s", err)
 	}
@@ -55,10 +109,10 @@ func (t testHTTPMethods) theResponseIs(ctx context.Context) error {
 }
 
 func (t testHTTPMethods) validJSON(ctx context.Context) error {
-	resp := ctx.Get(httpResponse{}).(Response)
+	resp := ctx.Get(ResponseKey{}).(Response)
 	body, err := ioutil.ReadAll(resp.Body)
 	resp.Body = ioutil.NopCloser(bytes.NewBuffer(body))
-	ctx.Set(httpResponse{}, resp)
+	ctx.Set(ResponseKey{}, resp)
 	if err != nil {
 		return fmt.Errorf("an error while reading the body: %s", err)
 	}
@@ -69,6 +123,17 @@ func (t testHTTPMethods) validJSON(ctx context.Context) error {
 	return nil
 }
 
+func (t testHTTPMethods) iMakeRequest(ctx context.Context) error {
+	req := ctx.Get(RequestKey{}).(*http.Request)
+	resp, err := t.tHTTP.MakeRequest(req)
+	if err != nil {
+		return err
+	}
+
+	ctx.Set(ResponseKey{}, resp)
+	return nil
+}
+
 func (t testHTTPMethods) makeRequest(ctx context.Context) error {
 	method := ctx.GetStringParam(0)
 	url := ctx.GetStringParam(1)
@@ -82,13 +147,13 @@ func (t testHTTPMethods) makeRequest(ctx context.Context) error {
 		return err
 	}
 
-	ctx.Set(httpResponse{}, resp)
+	ctx.Set(ResponseKey{}, resp)
 	return nil
 }
 
 func (t testHTTPMethods) statusCodeEquals(ctx context.Context) error {
 	expectedStatus := ctx.GetIntParam(0)
-	resp := ctx.Get(httpResponse{}).(Response)
+	resp := ctx.Get(ResponseKey{}).(Response)
 
 	if expectedStatus != resp.Code {
 		return fmt.Errorf("expected status code: %d but %d given", expectedStatus, resp.Code)
diff --git a/testhttp_test.go b/testhttp_test.go
index 2bf2f0c..df5d43f 100644
--- a/testhttp_test.go
+++ b/testhttp_test.go
@@ -1,6 +1,8 @@
 package gobdd
 
 import (
+	"github.com/go-bdd/assert"
+	"github.com/go-bdd/gobdd/context"
 	"net/http"
 	"testing"
 
@@ -19,7 +21,51 @@ func TestHTTP(t *testing.T) {
 		w.WriteHeader(http.StatusOK)
 	})
 
+	router.HandleFunc("/mirror", func(w http.ResponseWriter, r *http.Request) {
+		for key, values := range r.Header {
+			for _, value := range values {
+				w.Header().Add(key, value)
+			}
+		}
+		w.WriteHeader(http.StatusOK)
+	})
+
+	_ = s.AddStep(`^the request has method set to (GET|POST|PUT|DELETE|OPTIONS)$`, requestHasMethodSetTo)
+	_ = s.AddStep(`^the url is set to "([^"]*)"$`, urlIsSetTo)
+	_ = s.AddStep(`^the request body is nil$`, requestBodyIsNil)
+	_ = s.AddStep(`^I set the header "([^"]*)" to "([^"]*)"$`, ISetHeaderTo)
+	_ = s.AddStep(`^the request has header "([^"]*)" set to "([^"]*)"$`, requestHasHeaderSetTo)
 	testhttp.Build(s, router)
 
 	s.Run()
 }
+
+func requestHasMethodSetTo(ctx context.Context) error {
+	r := ctx.Get(testhttp.RequestKey{}).(*http.Request)
+	return assert.Equals(ctx.GetStringParam(0), r.Method)
+}
+
+func requestBodyIsNil(ctx context.Context) error {
+	r := ctx.Get(testhttp.RequestKey{}).(*http.Request)
+	return assert.Nil(r.Body)
+}
+
+func urlIsSetTo(ctx context.Context) error {
+	r := ctx.Get(testhttp.RequestKey{}).(*http.Request)
+	return assert.Equals(ctx.GetStringParam(0), r.URL.String())
+}
+
+func ISetHeaderTo(ctx context.Context) error {
+	r := ctx.Get(testhttp.RequestKey{}).(*http.Request)
+	r.Header.Set(ctx.GetStringParam(0), ctx.GetStringParam(1))
+	ctx.Set(testhttp.RequestKey{}, r)
+	return nil
+}
+
+func requestHasHeaderSetTo(ctx context.Context) 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)
+}