Repository: go-bdd/gobdd
Pull request #21 (2019-07-18 13:18:20)

add testHTTP package
Fixes https://github.com/go-bdd/gobdd/issues/7


Patch:
diff --git a/context.go b/context/context.go
similarity index 98%
rename from context.go
rename to context/context.go
index 33c1e83..5b51262 100644
--- a/context.go
+++ b/context/context.go
@@ -1,4 +1,4 @@
-package gobdd
+package context
 
 import (
 	"fmt"
@@ -14,7 +14,7 @@ type Context struct {
 	params [][]byte
 }
 
-func newContext() Context {
+func New() Context {
 	return Context{
 		values: map[interface{}]interface{}{},
 		params: [][]byte{},
@@ -258,6 +258,6 @@ func (ctx Context) GetInt64(key interface{}) int64 {
 	return value
 }
 
-func (ctx *Context) setParams(params [][]byte) {
+func (ctx *Context) SetParams(params [][]byte) {
 	ctx.params = params
 }
diff --git a/features/http.feature b/features/http.feature
new file mode 100644
index 0000000..7868bc3
--- /dev/null
+++ b/features/http.feature
@@ -0,0 +1,7 @@
+Feature: HTTP requests
+  Scenario: test GET request
+    When I make a GET request to "/health"
+    Then the response code equals 200
+  Scenario: not existing URI
+    When I make a GET request to "/not-exists"
+    Then the response code equals 404
\ No newline at end of file
diff --git a/gobdd.go b/gobdd.go
index 939246f..32697e0 100644
--- a/gobdd.go
+++ b/gobdd.go
@@ -13,9 +13,10 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/bkielbasa/gobdd/reporter"
-
 	"github.com/cucumber/gherkin-go"
+	"github.com/go-bdd/gobdd/context"
+	"github.com/go-bdd/gobdd/reporter"
+	"github.com/go-bdd/gobdd/step"
 )
 
 // Holds all the information about the suite (options, steps to execute etc)
@@ -53,12 +54,9 @@ func (options SuiteOptions) WithIgnoredTags(tags []string) SuiteOptions {
 	return options
 }
 
-// StepFunc every step function have to be compatible with this type
-type StepFunc func(ctx Context) error
-
 type stepDef struct {
 	expr *regexp.Regexp
-	f    StepFunc
+	f    step.Func
 }
 
 // Creates a new suites with given configuration and empty steps defined
@@ -81,7 +79,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 StepFunc) error {
+func (s *Suite) AddStep(step interface{}, f step.Func) error {
 	var regex *regexp.Regexp
 
 	switch t := step.(type) {
@@ -192,7 +190,7 @@ func (s *Suite) runScenarioOutline(outline *gherkin.ScenarioOutline, bkg *gherki
 		groups := ex.TableBody
 
 		for _, group := range groups {
-			ctx := newContext()
+			ctx := context.New()
 			if bkg != nil {
 				reporter.Background(bkg)
 				steps := s.getBackgroundSteps(bkg)
@@ -283,7 +281,7 @@ func (s *Suite) outlineDataTableArguments(t *gherkin.DataTable, placeholders []*
 
 func (s *Suite) runScenario(scenario *gherkin.Scenario, bkg *gherkin.Background, reporter reporter.Reporter) error {
 	reporter.Scenario(scenario)
-	ctx := newContext()
+	ctx := context.New()
 
 	if bkg != nil {
 		reporter.Background(bkg)
@@ -295,13 +293,13 @@ func (s *Suite) runScenario(scenario *gherkin.Scenario, bkg *gherkin.Background,
 	return nil
 }
 
-func (s *Suite) runSteps(ctx Context, reporter reporter.Reporter, steps []*gherkin.Step) {
+func (s *Suite) runSteps(ctx context.Context, reporter reporter.Reporter, steps []*gherkin.Step) {
 	for _, step := range steps {
 		s.runStep(ctx, reporter, step)
 	}
 }
 
-func (s *Suite) runStep(ctx Context, reporter reporter.Reporter, step *gherkin.Step) {
+func (s *Suite) runStep(ctx context.Context, reporter reporter.Reporter, step *gherkin.Step) {
 	defer func() {
 		if r := recover(); r != nil {
 			s.t.Error(r)
@@ -316,7 +314,7 @@ func (s *Suite) runStep(ctx Context, reporter reporter.Reporter, step *gherkin.S
 	}
 
 	b := def.expr.FindSubmatch([]byte(step.Text))
-	ctx.setParams(b[1:])
+	ctx.SetParams(b[1:])
 	err = def.f(ctx)
 	if err != nil {
 		reporter.FailedStep(step, err)
diff --git a/step/func.go b/step/func.go
new file mode 100644
index 0000000..86379e7
--- /dev/null
+++ b/step/func.go
@@ -0,0 +1,6 @@
+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/gobdd_test.go b/gobdd_test.go
index d354ddb..2544a0d 100644
--- a/gobdd_test.go
+++ b/gobdd_test.go
@@ -3,15 +3,17 @@ package gobdd
 import (
 	"errors"
 	"testing"
+
+	"github.com/go-bdd/gobdd/context"
 )
 
-func add(ctx Context) error {
+func add(ctx context.Context) error {
 	res := ctx.GetIntParam(0) + ctx.GetIntParam(1)
 	ctx.Set("sumRes", res)
 	return nil
 }
 
-func check(ctx Context) error {
+func check(ctx context.Context) error {
 	expected := ctx.GetIntParam(0)
 	received := ctx.GetInt("sumRes")
 
@@ -22,7 +24,7 @@ func check(ctx Context) error {
 	return nil
 }
 
-func fail(Context) error {
+func fail(context.Context) error {
 	return errors.New("the step should never be executed")
 }
 
@@ -43,14 +45,14 @@ 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) error {
+	err := suite.AddStep(`I add <digit1> and <digit2>`, func(ctx context.Context) error {
 		return nil
 	})
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	err = suite.AddStep(`the result should equal <result>`, func(ctx Context) error {
+	err = suite.AddStep(`the result should equal <result>`, func(ctx context.Context) error {
 
 		return nil
 	})
diff --git a/testhttp/requests.go b/testhttp/requests.go
new file mode 100644
index 0000000..52a9ce2
--- /dev/null
+++ b/testhttp/requests.go
@@ -0,0 +1,74 @@
+package testhttp
+
+import (
+	"errors"
+	"io"
+	"net/http"
+	"net/http/httptest"
+)
+
+type Response struct {
+	Code   int
+	Body   io.Reader
+	Header http.Header
+}
+
+type httpHandler interface {
+	ServeHTTP(w http.ResponseWriter, r *http.Request)
+}
+
+type TestHTTP struct {
+	handler httpHandler
+}
+
+func (thhtp TestHTTP) Trace(url string, body io.Reader) (Response, error) {
+	return thhtp.Request(http.MethodTrace, url, body)
+}
+
+func (thhtp TestHTTP) Options(url string, body io.Reader) (Response, error) {
+	return thhtp.Request(http.MethodOptions, url, body)
+}
+
+func (thhtp TestHTTP) Head(url string, body io.Reader) (Response, error) {
+	return thhtp.Request(http.MethodHead, url, body)
+}
+
+func (thhtp TestHTTP) Connect(url string, body io.Reader) (Response, error) {
+	return thhtp.Request(http.MethodConnect, url, body)
+}
+
+func (thhtp TestHTTP) Patch(url string, body io.Reader) (Response, error) {
+	return thhtp.Request(http.MethodPatch, url, body)
+}
+
+func (thhtp TestHTTP) Post(url string, body io.Reader) (Response, error) {
+	return thhtp.Request(http.MethodPost, url, body)
+}
+
+func (thhtp TestHTTP) Put(url string, body io.Reader) (Response, error) {
+	return thhtp.Request(http.MethodPut, url, body)
+}
+
+func (thhtp TestHTTP) Delete(url string, body io.Reader) (Response, error) {
+	return thhtp.Request(http.MethodDelete, url, body)
+}
+
+func (thhtp TestHTTP) Get(url string, body io.Reader) (Response, error) {
+	return thhtp.Request(http.MethodGet, url, body)
+}
+
+func (thhtp TestHTTP) Request(method, url string, body io.Reader) (Response, error) {
+	req, err := http.NewRequest(method, url, body)
+	if err != nil {
+		return Response{}, errors.New("invalid HTTP method type")
+	}
+
+	rr := httptest.NewRecorder()
+	thhtp.handler.ServeHTTP(rr, req)
+
+	return Response{
+		Code:   rr.Code,
+		Body:   rr.Body,
+		Header: rr.Header(),
+	}, nil
+}
diff --git a/testhttp/setup.go b/testhttp/setup.go
new file mode 100644
index 0000000..cce2167
--- /dev/null
+++ b/testhttp/setup.go
@@ -0,0 +1,7 @@
+package testhttp
+
+func Build(h httpHandler) TestHTTP {
+	return TestHTTP{
+		handler: h,
+	}
+}
diff --git a/testhttp_test.go b/testhttp_test.go
new file mode 100644
index 0000000..554ad3b
--- /dev/null
+++ b/testhttp_test.go
@@ -0,0 +1,55 @@
+package gobdd
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/go-bdd/gobdd/context"
+	"github.com/go-bdd/gobdd/testhttp"
+)
+
+type httpResponse struct{}
+
+type testHTTPMethods struct {
+	tHTTP testhttp.TestHTTP
+}
+
+func TestHTTP(t *testing.T) {
+	s := NewSuite(t, NewSuiteOptions().WithFeaturesPath("features/http.feature"))
+	router := http.NewServeMux()
+	router.HandleFunc("/health", func(w http.ResponseWriter, req *http.Request) {
+		w.WriteHeader(http.StatusOK)
+	})
+
+	tHTTP := testHTTPMethods{
+		tHTTP: testhttp.Build(router),
+	}
+
+	_ = s.AddStep(`^I make a (GET|POST|PUT|DELETE|OPTIONS) request to "([^"]*)"$`, tHTTP.makeRequest)
+	_ = s.AddStep(`^the response code equals (\d+)$`, tHTTP.statusCodeEquals)
+
+	s.Run()
+}
+
+func (t testHTTPMethods) makeRequest(ctx context.Context) error {
+	method := ctx.GetStringParam(0)
+	url := ctx.GetStringParam(1)
+	resp, err := t.tHTTP.Request(method, url, nil)
+	if err != nil {
+		return err
+	}
+
+	ctx.Set(httpResponse{}, resp)
+	return nil
+}
+
+func (t testHTTPMethods) statusCodeEquals(ctx context.Context) error {
+	expectedStatus := ctx.GetIntParam(0)
+	resp := ctx.Get(httpResponse{}).(testhttp.Response)
+
+	if expectedStatus != resp.Code {
+		return fmt.Errorf("expected status code: %d but %d given", expectedStatus, resp.Code)
+	}
+	return nil
+}