Skip to content

Commit

Permalink
Feature/testing t interface (#108)
Browse files Browse the repository at this point in the history
* abstract out *testing.T through an interface

* add in ginkgo test example

Co-authored-by: kuo.fong <[email protected]>
  • Loading branch information
anthonyfong100 and kuo.fong authored Mar 1, 2021
1 parent a809d7c commit d79f37b
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 36 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ go get -u github.com/steinfletcher/apitest
| [httprouter](https://github.com/steinfletcher/apitest/tree/master/examples/httprouter) | High performance HTTP request router that scales well |
| [mocks](https://github.com/steinfletcher/apitest/tree/master/examples/mocks) | example mocking out external http calls |
| [sequence diagrams](https://github.com/steinfletcher/apitest/tree/master/examples/sequence-diagrams) | generate sequence diagrams from tests. See the [demo](http://demo-html.apitest.dev.s3-website-eu-west-1.amazonaws.com/) |
| [Ginkgo](https://github.com/steinfletcher/apitest/tree/master/examples/ginkgo) | Ginkgo BDD test framework|

### Companion libraries

Expand Down
5 changes: 2 additions & 3 deletions apitest.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"runtime/debug"
"sort"
"strings"
"testing"
"time"
)

Expand Down Expand Up @@ -45,7 +44,7 @@ type APITest struct {
mocksObservers []Observe
recorderHook RecorderHook
mocks []*Mock
t *testing.T
t TestingT
httpClient *http.Client
transport *Transport
meta map[string]interface{}
Expand Down Expand Up @@ -466,7 +465,7 @@ func (r *Request) FormData(name string, values ...string) *Request {
}

// Expect marks the request spec as complete and following code will define the expected response
func (r *Request) Expect(t *testing.T) *Response {
func (r *Request) Expect(t TestingT) *Response {
r.apiTest.t = t
return r.apiTest.response
}
Expand Down
2 changes: 1 addition & 1 deletion apitest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1101,7 +1101,7 @@ func TestApiTest_ErrorIfMockInvocationsDoNotMatchTimes(t *testing.T) {
End()

verifier := mocks.NewVerifier()
verifier.FailFn = func(t *testing.T, failureMessage string, msgAndArgs ...interface{}) bool {
verifier.FailFn = func(t apitest.TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
assert.Equal(t, "mock was not invoked expected times", failureMessage)
return true
}
Expand Down
36 changes: 22 additions & 14 deletions assert.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,48 @@ package apitest

import (
"fmt"
"github.com/stretchr/testify/assert"
"net/http"
"testing"

"github.com/stretchr/testify/assert"
)

// TestingT is an interface to wrap the native *testing.T interface, this allows integration with GinkgoT() interface
// GinkgoT interface defined in https://github.com/onsi/ginkgo/blob/55c858784e51c26077949c81b6defb6b97b76944/ginkgo_dsl.go#L91
type TestingT interface {
Errorf(format string, args ...interface{})
Fatal(args ...interface{})
Fatalf(format string, args ...interface{})
}

// Verifier is the assertion interface allowing consumers to inject a custom assertion implementation.
// It also allows failure scenarios to be tested within apitest
type Verifier interface {
Equal(t *testing.T, expected, actual interface{}, msgAndArgs ...interface{}) bool
JSONEq(t *testing.T, expected string, actual string, msgAndArgs ...interface{}) bool
Fail(t *testing.T, failureMessage string, msgAndArgs ...interface{}) bool
NoError(t *testing.T, err error, msgAndArgs ...interface{}) bool
Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool
JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool
Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool
NoError(t TestingT, err error, msgAndArgs ...interface{}) bool
}

// testifyVerifier is a verifier that use https://github.com/stretchr/testify to perform assertions
type testifyVerifier struct{}

// JSONEq asserts that two JSON strings are equivalent
func (a testifyVerifier) JSONEq(t *testing.T, expected string, actual string, msgAndArgs ...interface{}) bool {
func (a testifyVerifier) JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
return assert.JSONEq(t, expected, actual, msgAndArgs...)
}

// Equal asserts that two objects are equal
func (a testifyVerifier) Equal(t *testing.T, expected, actual interface{}, msgAndArgs ...interface{}) bool {
func (a testifyVerifier) Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
return assert.Equal(t, expected, actual, msgAndArgs...)
}

// Fail reports a failure
func (a testifyVerifier) Fail(t *testing.T, failureMessage string, msgAndArgs ...interface{}) bool {
func (a testifyVerifier) Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
return assert.Fail(t, failureMessage, msgAndArgs...)
}

// NoError asserts that a function returned no error
func (a testifyVerifier) NoError(t *testing.T, err error, msgAndArgs ...interface{}) bool {
func (a testifyVerifier) NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
return assert.NoError(t, err, msgAndArgs...)
}

Expand All @@ -49,22 +57,22 @@ type NoopVerifier struct{}
var _ Verifier = NoopVerifier{}

// Equal does not perform any assertion and always returns true
func (n NoopVerifier) Equal(t *testing.T, expected, actual interface{}, msgAndArgs ...interface{}) bool {
func (n NoopVerifier) Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
return true
}

// JSONEq does not perform any assertion and always returns true
func (n NoopVerifier) JSONEq(t *testing.T, expected string, actual string, msgAndArgs ...interface{}) bool {
func (n NoopVerifier) JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
return true
}

// Fail does not perform any assertion and always returns true
func (n NoopVerifier) Fail(t *testing.T, failureMessage string, msgAndArgs ...interface{}) bool {
func (n NoopVerifier) Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
return true
}

// NoError asserts that a function returned no error
func (n NoopVerifier) NoError(t *testing.T, err error, msgAndArgs ...interface{}) bool {
func (n NoopVerifier) NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
return true
}

Expand Down
58 changes: 58 additions & 0 deletions examples/ginkgo/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package server

import (
"encoding/json"
"log"
"net/http"

"github.com/gorilla/mux"
)

type user struct {
ID string `json:"id"`
Name string `json:"name"`
}

type application struct {
Router *mux.Router
}

func NewApp() *application {
router := mux.NewRouter()
router.HandleFunc("/user/{id}", getUser()).Methods("GET")
return &application{Router: router}
}

func (a *application) start() {
log.Fatal(http.ListenAndServe("localhost:8888", a.Router))
}

func getUser() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
id := mux.Vars(r)["id"]
http.SetCookie(w, &http.Cookie{
Name: "TomsFavouriteDrink",
Value: "Beer",
Path: "/",
})

if id == "1234" {
user := &user{ID: id, Name: "Andy"}
bytes, _ := json.Marshal(user)
_, err := w.Write(bytes)
if err != nil {
panic(err)
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
return
}

w.WriteHeader(http.StatusNotFound)
}
}

func main() {
NewApp().start()
}
13 changes: 13 additions & 0 deletions examples/ginkgo/server_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package server_test

import (
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func TestHandlers(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Server Suite")
}
71 changes: 71 additions & 0 deletions examples/ginkgo/server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package server_test

import (
"net/http"

"github.com/gorilla/mux"
. "github.com/onsi/ginkgo"
"github.com/steinfletcher/apitest"
jsonpath "github.com/steinfletcher/apitest-jsonpath"
server "github.com/steinfletcher/apitest/examples/ginkgo"
)

var _ = Describe("Ginkgo/Server", func() {

var (
t GinkgoTInterface
router *mux.Router
)

BeforeEach(func() {
t = GinkgoT()
router = server.NewApp().Router
})

Context("Successful CookieMatching", func() {
It("cookies should be set correctly", func() {
apitest.New().
Handler(router).
Get("/user/1234").
Expect(t).
Cookies(apitest.NewCookie("TomsFavouriteDrink").
Value("Beer").
Path("/")).
Status(http.StatusOK).
End()
})
})

Context("Successful GetUser", func() {
It("Get User body should return desired value", func() {
apitest.New().
Handler(router).
Get("/user/1234").
Expect(t).
Body(`{"id": "1234", "name": "Andy"}`).
Status(http.StatusOK).
End()
})

It("Get User jsonpath should return desired value", func() {
apitest.New().
Handler(router).
Get("/user/1234").
Expect(t).
Assert(jsonpath.Equal(`$.id`, "1234")).
Status(http.StatusOK).
End()
})
})

Context("Unsuccessful GetUser", func() {
It("User not found error should be raised", func() {
apitest.New().
Handler(router).
Get("/user/1515").
Expect(t).
Status(http.StatusNotFound).
End()
})
})
})
7 changes: 2 additions & 5 deletions examples/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ require (
github.com/gin-gonic/gin v1.6.2
github.com/go-sql-driver/mysql v1.5.0
github.com/gofiber/fiber v1.11.0
github.com/golang/protobuf v1.4.0 // indirect
github.com/google/go-querystring v1.0.0 // indirect
github.com/gorilla/mux v1.7.4
github.com/imkira/go-interpol v1.1.0 // indirect
Expand All @@ -40,8 +39,8 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/moul/http2curl v1.0.0 // indirect
github.com/onsi/ginkgo v1.12.0 // indirect
github.com/onsi/gomega v1.9.0 // indirect
github.com/onsi/ginkgo v1.15.0
github.com/onsi/gomega v1.10.1
github.com/pkg/errors v0.9.1 // indirect
github.com/pressly/goose v2.6.0+incompatible
github.com/ryanuber/columnize v2.1.0+incompatible // indirect
Expand All @@ -56,8 +55,6 @@ require (
github.com/yudai/gojsondiff v1.0.0 // indirect
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
github.com/yudai/pp v2.0.1+incompatible // indirect
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 // indirect
golang.org/x/sys v0.0.0-20200427175716-29b57079015a // indirect
)

go 1.13
Loading

0 comments on commit d79f37b

Please sign in to comment.