From 8ec37a4cf9f69b7d7091df63f851aa91d25f572d Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 24 Nov 2016 10:45:36 +0000 Subject: [PATCH 1/2] Add a dedicated testutils package In the spirit of "if you have to do something 3 times then factor it out", make a testutils package to put all the `RoundTrip` boilerplate. I don't overly like having test packages, especially mixed in with code, but I don't see a nicer way of doing this without ending up with a sprawling mess of copypasta'd test boilerplate which will be an absolute nightmare to maintain. I think this is the lesser of two evils. --- .../go-neb/services/guggy/guggy_test.go | 17 +++++------------ .../go-neb/services/rssbot/rssbot_test.go | 17 +++++------------ .../go-neb/services/travisci/travisci_test.go | 17 +++++------------ .../matrix-org/go-neb/testutils/testutils.go | 15 +++++++++++++++ 4 files changed, 30 insertions(+), 36 deletions(-) create mode 100644 src/github.com/matrix-org/go-neb/testutils/testutils.go diff --git a/src/github.com/matrix-org/go-neb/services/guggy/guggy_test.go b/src/github.com/matrix-org/go-neb/services/guggy/guggy_test.go index b7d5a05..f79bf74 100644 --- a/src/github.com/matrix-org/go-neb/services/guggy/guggy_test.go +++ b/src/github.com/matrix-org/go-neb/services/guggy/guggy_test.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/matrix-org/go-neb/database" "github.com/matrix-org/go-neb/matrix" + "github.com/matrix-org/go-neb/testutils" "github.com/matrix-org/go-neb/types" "io/ioutil" "net/http" @@ -14,14 +15,6 @@ import ( "testing" ) -type MockTransport struct { - roundTrip func(*http.Request) (*http.Response, error) -} - -func (t MockTransport) RoundTrip(req *http.Request) (*http.Response, error) { - return t.roundTrip(req) -} - // TODO: It would be nice to tabularise this test so we can try failing different combinations of responses to make // sure all cases are handled, rather than just the general case as is here. func TestCommand(t *testing.T) { @@ -30,8 +23,8 @@ func TestCommand(t *testing.T) { guggyImageURL := "https://guggy.com/gifs/23ryf872fg" // Mock the response from Guggy - guggyTrans := struct{ MockTransport }{} - guggyTrans.roundTrip = func(req *http.Request) (*http.Response, error) { + guggyTrans := struct{ testutils.MockTransport }{} + guggyTrans.RT = func(req *http.Request) (*http.Response, error) { guggyURL := "https://text2gif.guggy.com/guggify" if req.URL.String() != guggyURL { t.Fatalf("Bad URL: got %s want %s", req.URL.String(), guggyURL) @@ -79,8 +72,8 @@ func TestCommand(t *testing.T) { guggy := srv.(*Service) // Mock the response from Matrix - matrixTrans := struct{ MockTransport }{} - matrixTrans.roundTrip = func(req *http.Request) (*http.Response, error) { + matrixTrans := struct{ testutils.MockTransport }{} + matrixTrans.RT = func(req *http.Request) (*http.Response, error) { if req.URL.String() == guggyImageURL { // getting the guggy image return &http.Response{ StatusCode: 200, diff --git a/src/github.com/matrix-org/go-neb/services/rssbot/rssbot_test.go b/src/github.com/matrix-org/go-neb/services/rssbot/rssbot_test.go index cc6b260..040b2ea 100644 --- a/src/github.com/matrix-org/go-neb/services/rssbot/rssbot_test.go +++ b/src/github.com/matrix-org/go-neb/services/rssbot/rssbot_test.go @@ -14,6 +14,7 @@ import ( "github.com/matrix-org/go-neb/database" "github.com/matrix-org/go-neb/matrix" + "github.com/matrix-org/go-neb/testutils" "github.com/matrix-org/go-neb/types" ) @@ -36,20 +37,12 @@ const rssFeedXML = ` ` -type MockTransport struct { - roundTrip func(*http.Request) (*http.Response, error) -} - -func (t MockTransport) RoundTrip(req *http.Request) (*http.Response, error) { - return t.roundTrip(req) -} - func TestHTMLEntities(t *testing.T) { database.SetServiceDB(&database.NopStorage{}) feedURL := "https://thehappymaskshop.hyrule" // Replace the cachingClient with a mock so we can intercept RSS requests - rssTrans := struct{ MockTransport }{} - rssTrans.roundTrip = func(req *http.Request) (*http.Response, error) { + rssTrans := struct{ testutils.MockTransport }{} + rssTrans.RT = func(req *http.Request) (*http.Response, error) { if req.URL.String() != feedURL { return nil, errors.New("Unknown test URL") } @@ -79,8 +72,8 @@ func TestHTMLEntities(t *testing.T) { // Create the Matrix client which will send the notification wg := sync.WaitGroup{} wg.Add(1) - matrixTrans := struct{ MockTransport }{} - matrixTrans.roundTrip = func(req *http.Request) (*http.Response, error) { + matrixTrans := struct{ testutils.MockTransport }{} + matrixTrans.RT = func(req *http.Request) (*http.Response, error) { if strings.HasPrefix(req.URL.Path, "/_matrix/client/r0/rooms/!linksroom:hyrule/send/m.room.message") { // Check content body to make sure it is decoded var msg matrix.HTMLMessage diff --git a/src/github.com/matrix-org/go-neb/services/travisci/travisci_test.go b/src/github.com/matrix-org/go-neb/services/travisci/travisci_test.go index 5a8e2e5..77b14e7 100644 --- a/src/github.com/matrix-org/go-neb/services/travisci/travisci_test.go +++ b/src/github.com/matrix-org/go-neb/services/travisci/travisci_test.go @@ -13,6 +13,7 @@ import ( "github.com/matrix-org/go-neb/database" "github.com/matrix-org/go-neb/matrix" + "github.com/matrix-org/go-neb/testutils" "github.com/matrix-org/go-neb/types" ) @@ -91,14 +92,6 @@ var travisTests = []struct { }, } -type MockTransport struct { - roundTrip func(*http.Request) (*http.Response, error) -} - -func (t MockTransport) RoundTrip(req *http.Request) (*http.Response, error) { - return t.roundTrip(req) -} - func TestTravisCI(t *testing.T) { database.SetServiceDB(&database.NopStorage{}) @@ -106,8 +99,8 @@ func TestTravisCI(t *testing.T) { urlToKey := make(map[string]string) urlToKey["https://api.travis-ci.org/config"] = travisOrgPEMPublicKey urlToKey["https://api.travis-ci.com/config"] = travisComPEMPublicKey - travisTransport := struct{ MockTransport }{} - travisTransport.roundTrip = func(req *http.Request) (*http.Response, error) { + travisTransport := struct{ testutils.MockTransport }{} + travisTransport.RT = func(req *http.Request) (*http.Response, error) { if key := urlToKey[req.URL.String()]; key != "" { escKey, _ := json.Marshal(key) return &http.Response{ @@ -124,8 +117,8 @@ func TestTravisCI(t *testing.T) { // Intercept message sending to Matrix and mock responses msgs := []matrix.TextMessage{} - matrixTrans := struct{ MockTransport }{} - matrixTrans.roundTrip = func(req *http.Request) (*http.Response, error) { + matrixTrans := struct{ testutils.MockTransport }{} + matrixTrans.RT = func(req *http.Request) (*http.Response, error) { if !strings.Contains(req.URL.String(), "/send/m.room.message") { return nil, fmt.Errorf("Unhandled URL: %s", req.URL.String()) } diff --git a/src/github.com/matrix-org/go-neb/testutils/testutils.go b/src/github.com/matrix-org/go-neb/testutils/testutils.go new file mode 100644 index 0000000..ba8508e --- /dev/null +++ b/src/github.com/matrix-org/go-neb/testutils/testutils.go @@ -0,0 +1,15 @@ +package testutils + +import ( + "net/http" +) + +// MockTransport implements RoundTripper +type MockTransport struct { + RT func(*http.Request) (*http.Response, error) +} + +// RoundTrip is a RoundTripper +func (t MockTransport) RoundTrip(req *http.Request) (*http.Response, error) { + return t.RT(req) +} From 683d36b5c505658f442c1cf713ece1ae1413c5f3 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 24 Nov 2016 16:10:12 +0000 Subject: [PATCH 2/2] Review comments --- .../matrix-org/go-neb/services/guggy/guggy_test.go | 5 ++--- .../go-neb/services/rssbot/rssbot_test.go | 5 ++--- .../go-neb/services/travisci/travisci_test.go | 5 ++--- .../matrix-org/go-neb/testutils/testutils.go | 13 +++++++++++++ 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/github.com/matrix-org/go-neb/services/guggy/guggy_test.go b/src/github.com/matrix-org/go-neb/services/guggy/guggy_test.go index f79bf74..480b8b8 100644 --- a/src/github.com/matrix-org/go-neb/services/guggy/guggy_test.go +++ b/src/github.com/matrix-org/go-neb/services/guggy/guggy_test.go @@ -23,8 +23,7 @@ func TestCommand(t *testing.T) { guggyImageURL := "https://guggy.com/gifs/23ryf872fg" // Mock the response from Guggy - guggyTrans := struct{ testutils.MockTransport }{} - guggyTrans.RT = func(req *http.Request) (*http.Response, error) { + guggyTrans := testutils.NewRoundTripper(func(req *http.Request) (*http.Response, error) { guggyURL := "https://text2gif.guggy.com/guggify" if req.URL.String() != guggyURL { t.Fatalf("Bad URL: got %s want %s", req.URL.String(), guggyURL) @@ -58,7 +57,7 @@ func TestCommand(t *testing.T) { StatusCode: 200, Body: ioutil.NopCloser(bytes.NewBuffer(b)), }, nil - } + }) // clobber the guggy service http client instance httpClient = &http.Client{Transport: guggyTrans} diff --git a/src/github.com/matrix-org/go-neb/services/rssbot/rssbot_test.go b/src/github.com/matrix-org/go-neb/services/rssbot/rssbot_test.go index 040b2ea..4912d8c 100644 --- a/src/github.com/matrix-org/go-neb/services/rssbot/rssbot_test.go +++ b/src/github.com/matrix-org/go-neb/services/rssbot/rssbot_test.go @@ -41,8 +41,7 @@ func TestHTMLEntities(t *testing.T) { database.SetServiceDB(&database.NopStorage{}) feedURL := "https://thehappymaskshop.hyrule" // Replace the cachingClient with a mock so we can intercept RSS requests - rssTrans := struct{ testutils.MockTransport }{} - rssTrans.RT = func(req *http.Request) (*http.Response, error) { + rssTrans := testutils.NewRoundTripper(func(req *http.Request) (*http.Response, error) { if req.URL.String() != feedURL { return nil, errors.New("Unknown test URL") } @@ -50,7 +49,7 @@ func TestHTMLEntities(t *testing.T) { StatusCode: 200, Body: ioutil.NopCloser(bytes.NewBufferString(rssFeedXML)), }, nil - } + }) cachingClient = &http.Client{Transport: rssTrans} // Create the RSS service diff --git a/src/github.com/matrix-org/go-neb/services/travisci/travisci_test.go b/src/github.com/matrix-org/go-neb/services/travisci/travisci_test.go index 77b14e7..3c1d695 100644 --- a/src/github.com/matrix-org/go-neb/services/travisci/travisci_test.go +++ b/src/github.com/matrix-org/go-neb/services/travisci/travisci_test.go @@ -99,8 +99,7 @@ func TestTravisCI(t *testing.T) { urlToKey := make(map[string]string) urlToKey["https://api.travis-ci.org/config"] = travisOrgPEMPublicKey urlToKey["https://api.travis-ci.com/config"] = travisComPEMPublicKey - travisTransport := struct{ testutils.MockTransport }{} - travisTransport.RT = func(req *http.Request) (*http.Response, error) { + travisTransport := testutils.NewRoundTripper(func(req *http.Request) (*http.Response, error) { if key := urlToKey[req.URL.String()]; key != "" { escKey, _ := json.Marshal(key) return &http.Response{ @@ -111,7 +110,7 @@ func TestTravisCI(t *testing.T) { }, nil } return nil, fmt.Errorf("Unhandled URL %s", req.URL.String()) - } + }) // clobber the http client that the service uses to talk to Travis httpClient = &http.Client{Transport: travisTransport} diff --git a/src/github.com/matrix-org/go-neb/testutils/testutils.go b/src/github.com/matrix-org/go-neb/testutils/testutils.go index ba8508e..ee678fa 100644 --- a/src/github.com/matrix-org/go-neb/testutils/testutils.go +++ b/src/github.com/matrix-org/go-neb/testutils/testutils.go @@ -6,6 +6,12 @@ import ( // MockTransport implements RoundTripper type MockTransport struct { + // RT is the RoundTrip function. Replace this function with your test function. + // For example: + // t := MockTransport{} + // t.RT = func(req *http.Request) (*http.Response, error) { + // // assert req args, return res or error + // } RT func(*http.Request) (*http.Response, error) } @@ -13,3 +19,10 @@ type MockTransport struct { func (t MockTransport) RoundTrip(req *http.Request) (*http.Response, error) { return t.RT(req) } + +// NewRoundTripper returns a new RoundTripper which will call the provided function. +func NewRoundTripper(roundTrip func(*http.Request) (*http.Response, error)) http.RoundTripper { + rt := MockTransport{} + rt.RT = roundTrip + return rt +}