From 0044ecf5273cacf8151a2de049df93169f6088a9 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Tue, 16 Jun 2026 06:38:52 +0000 Subject: [PATCH 1/4] Add fluent HTTP response assertions --- hammy/httpassert/httpassert.go | 53 +++++++++++++++++++++++++++-- hammy/httpassert/httpassert_test.go | 21 ++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/hammy/httpassert/httpassert.go b/hammy/httpassert/httpassert.go index 5e9c04d..ce0dbe7 100644 --- a/hammy/httpassert/httpassert.go +++ b/hammy/httpassert/httpassert.go @@ -10,7 +10,20 @@ import ( "github.com/gogunit/gunit/hammy" ) +func Response(resp *http.Response) *Resp { + return &Resp{actual: resp} +} + +type Resp struct { + actual *http.Response +} + func Status(resp *http.Response, expected int) hammy.AssertionMessage { + return Response(resp).Status(expected) +} + +func (r *Resp) Status(expected int) hammy.AssertionMessage { + resp := r.response() if resp == nil { return hammy.Assert(false, "got nil response, wanted status <%d>", expected) } @@ -18,6 +31,11 @@ func Status(resp *http.Response, expected int) hammy.AssertionMessage { } func StatusInRange(resp *http.Response, min, max int) hammy.AssertionMessage { + return Response(resp).StatusInRange(min, max) +} + +func (r *Resp) StatusInRange(min, max int) hammy.AssertionMessage { + resp := r.response() if min > max { return hammy.Assert(false, "got invalid status range <%d..%d>", min, max) } @@ -28,6 +46,11 @@ func StatusInRange(resp *http.Response, min, max int) hammy.AssertionMessage { } func Header(resp *http.Response, key, expected string) hammy.AssertionMessage { + return Response(resp).Header(key, expected) +} + +func (r *Resp) Header(key, expected string) hammy.AssertionMessage { + resp := r.response() if resp == nil { return hammy.Assert(false, "got nil response, wanted header <%s> equal to <%s>", key, expected) } @@ -36,6 +59,11 @@ func Header(resp *http.Response, key, expected string) hammy.AssertionMessage { } func HeaderContains(resp *http.Response, key, expected string) hammy.AssertionMessage { + return Response(resp).HeaderContains(key, expected) +} + +func (r *Resp) HeaderContains(key, expected string) hammy.AssertionMessage { + resp := r.response() if resp == nil { return hammy.Assert(false, "got nil response, wanted header <%s> containing <%s>", key, expected) } @@ -44,7 +72,11 @@ func HeaderContains(resp *http.Response, key, expected string) hammy.AssertionMe } func BodyEqual(resp *http.Response, expected string) hammy.AssertionMessage { - actual, result := readBody(resp) + return Response(resp).BodyEqual(expected) +} + +func (r *Resp) BodyEqual(expected string) hammy.AssertionMessage { + actual, result := readBody(r.response()) if !result.IsSuccessful { return result } @@ -52,7 +84,11 @@ func BodyEqual(resp *http.Response, expected string) hammy.AssertionMessage { } func BodyContains(resp *http.Response, expected string) hammy.AssertionMessage { - actual, result := readBody(resp) + return Response(resp).BodyContains(expected) +} + +func (r *Resp) BodyContains(expected string) hammy.AssertionMessage { + actual, result := readBody(r.response()) if !result.IsSuccessful { return result } @@ -60,7 +96,11 @@ func BodyContains(resp *http.Response, expected string) hammy.AssertionMessage { } func BodyMatchesRegexp(resp *http.Response, pattern string) hammy.AssertionMessage { - actual, result := readBody(resp) + return Response(resp).BodyMatchesRegexp(pattern) +} + +func (r *Resp) BodyMatchesRegexp(pattern string) hammy.AssertionMessage { + actual, result := readBody(r.response()) if !result.IsSuccessful { return result } @@ -72,6 +112,13 @@ func BodyMatchesRegexp(resp *http.Response, pattern string) hammy.AssertionMessa return hammy.Assert(re.MatchString(actual), "got body <%s>, wanted regexp <%s>", actual, pattern) } +func (r *Resp) response() *http.Response { + if r == nil { + return nil + } + return r.actual +} + func readBody(resp *http.Response) (string, hammy.AssertionMessage) { if resp == nil { return "", hammy.Assert(false, "got nil response, wanted response body") diff --git a/hammy/httpassert/httpassert_test.go b/hammy/httpassert/httpassert_test.go index 929ec84..ae57e85 100644 --- a/hammy/httpassert/httpassert_test.go +++ b/hammy/httpassert/httpassert_test.go @@ -182,3 +182,24 @@ func (errorReadCloser) Close() error { } var _ io.ReadCloser = errorReadCloser{} + +func Test_Response_methods_success(t *testing.T) { + assert := hammy.New(t) + resp := newResponse(http.StatusCreated, http.Header{"Content-Type": {"application/json; charset=utf-8"}}, "status 201") + actual := httpassert.Response(resp) + + assert.Is(actual.Status(http.StatusCreated)) + assert.Is(actual.StatusInRange(200, 299)) + assert.Is(actual.Header("Content-Type", "application/json; charset=utf-8")) + assert.Is(actual.HeaderContains("Content-Type", "application/json")) + assert.Is(actual.BodyEqual("status 201")) + assert.Is(actual.BodyContains("201")) + assert.Is(actual.BodyMatchesRegexp(`status \d+`)) +} + +func Test_Response_methods_failure_nil_receiver(t *testing.T) { + var resp *httpassert.Resp + result := resp.Status(http.StatusOK) + + requireFailure(t, result, "got nil response") +} From bf7cdcff052e2e9911872e8cd343dbc63ea12399 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Tue, 16 Jun 2026 07:29:02 +0000 Subject: [PATCH 2/4] Split fluent response assertion tests --- hammy/httpassert/httpassert_test.go | 57 +++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/hammy/httpassert/httpassert_test.go b/hammy/httpassert/httpassert_test.go index ae57e85..9fe7290 100644 --- a/hammy/httpassert/httpassert_test.go +++ b/hammy/httpassert/httpassert_test.go @@ -183,18 +183,53 @@ func (errorReadCloser) Close() error { var _ io.ReadCloser = errorReadCloser{} -func Test_Response_methods_success(t *testing.T) { +func Test_Response_Status_success(t *testing.T) { assert := hammy.New(t) - resp := newResponse(http.StatusCreated, http.Header{"Content-Type": {"application/json; charset=utf-8"}}, "status 201") - actual := httpassert.Response(resp) - - assert.Is(actual.Status(http.StatusCreated)) - assert.Is(actual.StatusInRange(200, 299)) - assert.Is(actual.Header("Content-Type", "application/json; charset=utf-8")) - assert.Is(actual.HeaderContains("Content-Type", "application/json")) - assert.Is(actual.BodyEqual("status 201")) - assert.Is(actual.BodyContains("201")) - assert.Is(actual.BodyMatchesRegexp(`status \d+`)) + resp := newResponse(http.StatusCreated, nil, "") + + assert.Is(httpassert.Response(resp).Status(http.StatusCreated)) +} + +func Test_Response_StatusInRange_success(t *testing.T) { + assert := hammy.New(t) + resp := newResponse(http.StatusNoContent, nil, "") + + assert.Is(httpassert.Response(resp).StatusInRange(200, 299)) +} + +func Test_Response_Header_success(t *testing.T) { + assert := hammy.New(t) + resp := newResponse(http.StatusOK, http.Header{"Content-Type": {"application/json; charset=utf-8"}}, "") + + assert.Is(httpassert.Response(resp).Header("Content-Type", "application/json; charset=utf-8")) +} + +func Test_Response_HeaderContains_success(t *testing.T) { + assert := hammy.New(t) + resp := newResponse(http.StatusOK, http.Header{"Content-Type": {"application/json; charset=utf-8"}}, "") + + assert.Is(httpassert.Response(resp).HeaderContains("Content-Type", "application/json")) +} + +func Test_Response_BodyEqual_success(t *testing.T) { + assert := hammy.New(t) + resp := newResponse(http.StatusOK, nil, "status 201") + + assert.Is(httpassert.Response(resp).BodyEqual("status 201")) +} + +func Test_Response_BodyContains_success(t *testing.T) { + assert := hammy.New(t) + resp := newResponse(http.StatusOK, nil, "status 201") + + assert.Is(httpassert.Response(resp).BodyContains("201")) +} + +func Test_Response_BodyMatchesRegexp_success(t *testing.T) { + assert := hammy.New(t) + resp := newResponse(http.StatusOK, nil, "status 201") + + assert.Is(httpassert.Response(resp).BodyMatchesRegexp(`status \d+`)) } func Test_Response_methods_failure_nil_receiver(t *testing.T) { From e02b568648d410d45c85ef4f3350c2ff13a47aec Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Tue, 16 Jun 2026 18:48:36 +0000 Subject: [PATCH 3/4] Remove non-fluent HTTP assert functions --- hammy/httpassert/example_test.go | 28 ++++----- hammy/httpassert/httpassert.go | 28 --------- hammy/httpassert/httpassert_test.go | 93 +++++++---------------------- 3 files changed, 36 insertions(+), 113 deletions(-) diff --git a/hammy/httpassert/example_test.go b/hammy/httpassert/example_test.go index cc1aec1..71e7bda 100644 --- a/hammy/httpassert/example_test.go +++ b/hammy/httpassert/example_test.go @@ -9,52 +9,52 @@ import ( "github.com/gogunit/gunit/hammy/httpassert" ) -func ExampleStatus() { - printExample(httpassert.Status(exampleResponse(http.StatusCreated, nil, ""), http.StatusCreated)) +func ExampleResp_Status() { + printExample(httpassert.Response(exampleResponse(http.StatusCreated, nil, "")).Status(http.StatusCreated)) // Output: // message="got status <201>, wanted <201>" // success=true } -func ExampleStatusInRange() { - printExample(httpassert.StatusInRange(exampleResponse(http.StatusNoContent, nil, ""), 200, 299)) +func ExampleResp_StatusInRange() { + printExample(httpassert.Response(exampleResponse(http.StatusNoContent, nil, "")).StatusInRange(200, 299)) // Output: // message="got status <204>, wanted in range <200..299>" // success=true } -func ExampleHeader() { +func ExampleResp_Header() { resp := exampleResponse(http.StatusOK, http.Header{"Content-Type": {"application/json"}}, "") - printExample(httpassert.Header(resp, "Content-Type", "application/json")) + printExample(httpassert.Response(resp).Header("Content-Type", "application/json")) // Output: // message="got header =, wanted " // success=true } -func ExampleHeaderContains() { +func ExampleResp_HeaderContains() { resp := exampleResponse(http.StatusOK, http.Header{"Content-Type": {"application/json; charset=utf-8"}}, "") - printExample(httpassert.HeaderContains(resp, "Content-Type", "application/json")) + printExample(httpassert.Response(resp).HeaderContains("Content-Type", "application/json")) // Output: // message="got header =, wanted containing " // success=true } -func ExampleBodyEqual() { - printExample(httpassert.BodyEqual(exampleResponse(http.StatusOK, nil, "hello world"), "hello world")) +func ExampleResp_BodyEqual() { + printExample(httpassert.Response(exampleResponse(http.StatusOK, nil, "hello world")).BodyEqual("hello world")) // Output: // message="got body , wanted equal to " // success=true } -func ExampleBodyContains() { - printExample(httpassert.BodyContains(exampleResponse(http.StatusOK, nil, "hello world"), "world")) +func ExampleResp_BodyContains() { + printExample(httpassert.Response(exampleResponse(http.StatusOK, nil, "hello world")).BodyContains("world")) // Output: // message="got body , wanted containing " // success=true } -func ExampleBodyMatchesRegexp() { - printExample(httpassert.BodyMatchesRegexp(exampleResponse(http.StatusOK, nil, "status 204"), `status \d+`)) +func ExampleResp_BodyMatchesRegexp() { + printExample(httpassert.Response(exampleResponse(http.StatusOK, nil, "status 204")).BodyMatchesRegexp(`status \d+`)) // Output: // message="got body , wanted regexp " // success=true diff --git a/hammy/httpassert/httpassert.go b/hammy/httpassert/httpassert.go index ce0dbe7..bbccf2b 100644 --- a/hammy/httpassert/httpassert.go +++ b/hammy/httpassert/httpassert.go @@ -18,10 +18,6 @@ type Resp struct { actual *http.Response } -func Status(resp *http.Response, expected int) hammy.AssertionMessage { - return Response(resp).Status(expected) -} - func (r *Resp) Status(expected int) hammy.AssertionMessage { resp := r.response() if resp == nil { @@ -30,10 +26,6 @@ func (r *Resp) Status(expected int) hammy.AssertionMessage { return hammy.Assert(resp.StatusCode == expected, "got status <%d>, wanted <%d>", resp.StatusCode, expected) } -func StatusInRange(resp *http.Response, min, max int) hammy.AssertionMessage { - return Response(resp).StatusInRange(min, max) -} - func (r *Resp) StatusInRange(min, max int) hammy.AssertionMessage { resp := r.response() if min > max { @@ -45,10 +37,6 @@ func (r *Resp) StatusInRange(min, max int) hammy.AssertionMessage { return hammy.Assert(resp.StatusCode >= min && resp.StatusCode <= max, "got status <%d>, wanted in range <%d..%d>", resp.StatusCode, min, max) } -func Header(resp *http.Response, key, expected string) hammy.AssertionMessage { - return Response(resp).Header(key, expected) -} - func (r *Resp) Header(key, expected string) hammy.AssertionMessage { resp := r.response() if resp == nil { @@ -58,10 +46,6 @@ func (r *Resp) Header(key, expected string) hammy.AssertionMessage { return hammy.Assert(actual == expected, "got header <%s>=<%s>, wanted <%s>", key, actual, expected) } -func HeaderContains(resp *http.Response, key, expected string) hammy.AssertionMessage { - return Response(resp).HeaderContains(key, expected) -} - func (r *Resp) HeaderContains(key, expected string) hammy.AssertionMessage { resp := r.response() if resp == nil { @@ -71,10 +55,6 @@ func (r *Resp) HeaderContains(key, expected string) hammy.AssertionMessage { return hammy.Assert(strings.Contains(actual, expected), "got header <%s>=<%s>, wanted containing <%s>", key, actual, expected) } -func BodyEqual(resp *http.Response, expected string) hammy.AssertionMessage { - return Response(resp).BodyEqual(expected) -} - func (r *Resp) BodyEqual(expected string) hammy.AssertionMessage { actual, result := readBody(r.response()) if !result.IsSuccessful { @@ -83,10 +63,6 @@ func (r *Resp) BodyEqual(expected string) hammy.AssertionMessage { return hammy.Assert(actual == expected, "got body <%s>, wanted equal to <%s>", actual, expected) } -func BodyContains(resp *http.Response, expected string) hammy.AssertionMessage { - return Response(resp).BodyContains(expected) -} - func (r *Resp) BodyContains(expected string) hammy.AssertionMessage { actual, result := readBody(r.response()) if !result.IsSuccessful { @@ -95,10 +71,6 @@ func (r *Resp) BodyContains(expected string) hammy.AssertionMessage { return hammy.Assert(strings.Contains(actual, expected), "got body <%s>, wanted containing <%s>", actual, expected) } -func BodyMatchesRegexp(resp *http.Response, pattern string) hammy.AssertionMessage { - return Response(resp).BodyMatchesRegexp(pattern) -} - func (r *Resp) BodyMatchesRegexp(pattern string) hammy.AssertionMessage { actual, result := readBody(r.response()) if !result.IsSuccessful { diff --git a/hammy/httpassert/httpassert_test.go b/hammy/httpassert/httpassert_test.go index 9fe7290..08230b1 100644 --- a/hammy/httpassert/httpassert_test.go +++ b/hammy/httpassert/httpassert_test.go @@ -16,17 +16,17 @@ func Test_Status_success(t *testing.T) { assert := hammy.New(t) resp := newResponse(http.StatusCreated, nil, "") - assert.Is(httpassert.Status(resp, http.StatusCreated)) + assert.Is(httpassert.Response(resp).Status(http.StatusCreated)) } func Test_Status_failure(t *testing.T) { - result := httpassert.Status(newResponse(http.StatusCreated, nil, ""), http.StatusOK) + result := httpassert.Response(newResponse(http.StatusCreated, nil, "")).Status(http.StatusOK) requireFailure(t, result, "got status <201>, wanted <200>") } func Test_Status_failure_nil_response(t *testing.T) { - result := httpassert.Status(nil, http.StatusOK) + result := httpassert.Response(nil).Status(http.StatusOK) requireFailure(t, result, "got nil response") } @@ -35,17 +35,17 @@ func Test_StatusInRange_success(t *testing.T) { assert := hammy.New(t) resp := newResponse(http.StatusNoContent, nil, "") - assert.Is(httpassert.StatusInRange(resp, 200, 299)) + assert.Is(httpassert.Response(resp).StatusInRange(200, 299)) } func Test_StatusInRange_failure(t *testing.T) { - result := httpassert.StatusInRange(newResponse(http.StatusBadRequest, nil, ""), 200, 299) + result := httpassert.Response(newResponse(http.StatusBadRequest, nil, "")).StatusInRange(200, 299) requireFailure(t, result, "got status <400>, wanted in range <200..299>") } func Test_StatusInRange_failure_invalid_range(t *testing.T) { - result := httpassert.StatusInRange(newResponse(http.StatusOK, nil, ""), 299, 200) + result := httpassert.Response(newResponse(http.StatusOK, nil, "")).StatusInRange(299, 200) requireFailure(t, result, "got invalid status range <299..200>") } @@ -54,12 +54,12 @@ func Test_Header_success(t *testing.T) { assert := hammy.New(t) resp := newResponse(http.StatusOK, http.Header{"Content-Type": {"application/json"}}, "") - assert.Is(httpassert.Header(resp, "Content-Type", "application/json")) + assert.Is(httpassert.Response(resp).Header("Content-Type", "application/json")) } func Test_Header_failure(t *testing.T) { resp := newResponse(http.StatusOK, http.Header{"Content-Type": {"text/plain"}}, "") - result := httpassert.Header(resp, "Content-Type", "application/json") + result := httpassert.Response(resp).Header("Content-Type", "application/json") requireFailure(t, result, "got header =, wanted ") } @@ -68,12 +68,12 @@ func Test_HeaderContains_success(t *testing.T) { assert := hammy.New(t) resp := newResponse(http.StatusOK, http.Header{"Content-Type": {"application/json; charset=utf-8"}}, "") - assert.Is(httpassert.HeaderContains(resp, "Content-Type", "application/json")) + assert.Is(httpassert.Response(resp).HeaderContains("Content-Type", "application/json")) } func Test_HeaderContains_failure(t *testing.T) { resp := newResponse(http.StatusOK, http.Header{"Content-Type": {"text/plain"}}, "") - result := httpassert.HeaderContains(resp, "Content-Type", "application/json") + result := httpassert.Response(resp).HeaderContains("Content-Type", "application/json") requireFailure(t, result, "got header =, wanted containing ") } @@ -82,12 +82,12 @@ func Test_BodyEqual_success(t *testing.T) { assert := hammy.New(t) resp := newResponse(http.StatusOK, nil, "hello world") - assert.Is(httpassert.BodyEqual(resp, "hello world")) + assert.Is(httpassert.Response(resp).BodyEqual("hello world")) } func Test_BodyEqual_failure(t *testing.T) { resp := newResponse(http.StatusOK, nil, "hello world") - result := httpassert.BodyEqual(resp, "goodbye") + result := httpassert.Response(resp).BodyEqual("goodbye") requireFailure(t, result, "got body , wanted equal to ") } @@ -96,12 +96,12 @@ func Test_BodyContains_success(t *testing.T) { assert := hammy.New(t) resp := newResponse(http.StatusOK, nil, "hello world") - assert.Is(httpassert.BodyContains(resp, "world")) + assert.Is(httpassert.Response(resp).BodyContains("world")) } func Test_BodyContains_failure(t *testing.T) { resp := newResponse(http.StatusOK, nil, "hello world") - result := httpassert.BodyContains(resp, "goodbye") + result := httpassert.Response(resp).BodyContains("goodbye") requireFailure(t, result, "got body , wanted containing ") } @@ -110,19 +110,19 @@ func Test_BodyMatchesRegexp_success(t *testing.T) { assert := hammy.New(t) resp := newResponse(http.StatusOK, nil, "status 204") - assert.Is(httpassert.BodyMatchesRegexp(resp, `status \d+`)) + assert.Is(httpassert.Response(resp).BodyMatchesRegexp(`status \d+`)) } func Test_BodyMatchesRegexp_failure(t *testing.T) { resp := newResponse(http.StatusOK, nil, "status 204") - result := httpassert.BodyMatchesRegexp(resp, `status 5\d\d`) + result := httpassert.Response(resp).BodyMatchesRegexp(`status 5\d\d`) requireFailure(t, result, "got body , wanted regexp ") } func Test_BodyMatchesRegexp_failure_invalid_pattern(t *testing.T) { resp := newResponse(http.StatusOK, nil, "status 204") - result := httpassert.BodyMatchesRegexp(resp, `(`) + result := httpassert.Response(resp).BodyMatchesRegexp(`(`) requireFailure(t, result, "invalid regexp <(>") } @@ -131,20 +131,20 @@ func Test_Body_assertions_restore_body(t *testing.T) { assert := hammy.New(t) resp := newResponse(http.StatusOK, nil, "hello world") - assert.Is(httpassert.BodyContains(resp, "hello")) - assert.Is(httpassert.BodyEqual(resp, "hello world")) - assert.Is(httpassert.BodyMatchesRegexp(resp, `world$`)) + assert.Is(httpassert.Response(resp).BodyContains("hello")) + assert.Is(httpassert.Response(resp).BodyEqual("hello world")) + assert.Is(httpassert.Response(resp).BodyMatchesRegexp(`world$`)) } func Test_Body_assertion_failure_nil_response(t *testing.T) { - result := httpassert.BodyEqual(nil, "hello") + result := httpassert.Response(nil).BodyEqual("hello") requireFailure(t, result, "got nil response") } func Test_Body_assertion_failure_read_error(t *testing.T) { resp := &http.Response{Body: errorReadCloser{}} - result := httpassert.BodyEqual(resp, "hello") + result := httpassert.Response(resp).BodyEqual("hello") requireFailure(t, result, "got body read error: read failed") } @@ -183,55 +183,6 @@ func (errorReadCloser) Close() error { var _ io.ReadCloser = errorReadCloser{} -func Test_Response_Status_success(t *testing.T) { - assert := hammy.New(t) - resp := newResponse(http.StatusCreated, nil, "") - - assert.Is(httpassert.Response(resp).Status(http.StatusCreated)) -} - -func Test_Response_StatusInRange_success(t *testing.T) { - assert := hammy.New(t) - resp := newResponse(http.StatusNoContent, nil, "") - - assert.Is(httpassert.Response(resp).StatusInRange(200, 299)) -} - -func Test_Response_Header_success(t *testing.T) { - assert := hammy.New(t) - resp := newResponse(http.StatusOK, http.Header{"Content-Type": {"application/json; charset=utf-8"}}, "") - - assert.Is(httpassert.Response(resp).Header("Content-Type", "application/json; charset=utf-8")) -} - -func Test_Response_HeaderContains_success(t *testing.T) { - assert := hammy.New(t) - resp := newResponse(http.StatusOK, http.Header{"Content-Type": {"application/json; charset=utf-8"}}, "") - - assert.Is(httpassert.Response(resp).HeaderContains("Content-Type", "application/json")) -} - -func Test_Response_BodyEqual_success(t *testing.T) { - assert := hammy.New(t) - resp := newResponse(http.StatusOK, nil, "status 201") - - assert.Is(httpassert.Response(resp).BodyEqual("status 201")) -} - -func Test_Response_BodyContains_success(t *testing.T) { - assert := hammy.New(t) - resp := newResponse(http.StatusOK, nil, "status 201") - - assert.Is(httpassert.Response(resp).BodyContains("201")) -} - -func Test_Response_BodyMatchesRegexp_success(t *testing.T) { - assert := hammy.New(t) - resp := newResponse(http.StatusOK, nil, "status 201") - - assert.Is(httpassert.Response(resp).BodyMatchesRegexp(`status \d+`)) -} - func Test_Response_methods_failure_nil_receiver(t *testing.T) { var resp *httpassert.Resp result := resp.Status(http.StatusOK) From 38517cd2e17533747adb8e458cf78d370a9bec37 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Tue, 16 Jun 2026 18:48:56 +0000 Subject: [PATCH 4/4] Use spy pattern in httpassert tests --- hammy/httpassert/httpassert_test.go | 64 ++++++++++++++++++----------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/hammy/httpassert/httpassert_test.go b/hammy/httpassert/httpassert_test.go index 08230b1..5d92b26 100644 --- a/hammy/httpassert/httpassert_test.go +++ b/hammy/httpassert/httpassert_test.go @@ -5,9 +5,9 @@ import ( "io" "net/http" "net/http/httptest" - "strings" "testing" + "github.com/gogunit/gunit/eye" "github.com/gogunit/gunit/hammy" "github.com/gogunit/gunit/hammy/httpassert" ) @@ -22,13 +22,17 @@ func Test_Status_success(t *testing.T) { func Test_Status_failure(t *testing.T) { result := httpassert.Response(newResponse(http.StatusCreated, nil, "")).Status(http.StatusOK) - requireFailure(t, result, "got status <201>, wanted <200>") + spy := eye.Spy() + hammy.New(spy).Is(result) + spy.HadErrorContaining(t, "got status <201>, wanted <200>") } func Test_Status_failure_nil_response(t *testing.T) { result := httpassert.Response(nil).Status(http.StatusOK) - requireFailure(t, result, "got nil response") + spy := eye.Spy() + hammy.New(spy).Is(result) + spy.HadErrorContaining(t, "got nil response") } func Test_StatusInRange_success(t *testing.T) { @@ -41,13 +45,17 @@ func Test_StatusInRange_success(t *testing.T) { func Test_StatusInRange_failure(t *testing.T) { result := httpassert.Response(newResponse(http.StatusBadRequest, nil, "")).StatusInRange(200, 299) - requireFailure(t, result, "got status <400>, wanted in range <200..299>") + spy := eye.Spy() + hammy.New(spy).Is(result) + spy.HadErrorContaining(t, "got status <400>, wanted in range <200..299>") } func Test_StatusInRange_failure_invalid_range(t *testing.T) { result := httpassert.Response(newResponse(http.StatusOK, nil, "")).StatusInRange(299, 200) - requireFailure(t, result, "got invalid status range <299..200>") + spy := eye.Spy() + hammy.New(spy).Is(result) + spy.HadErrorContaining(t, "got invalid status range <299..200>") } func Test_Header_success(t *testing.T) { @@ -61,7 +69,9 @@ func Test_Header_failure(t *testing.T) { resp := newResponse(http.StatusOK, http.Header{"Content-Type": {"text/plain"}}, "") result := httpassert.Response(resp).Header("Content-Type", "application/json") - requireFailure(t, result, "got header =, wanted ") + spy := eye.Spy() + hammy.New(spy).Is(result) + spy.HadErrorContaining(t, "got header =, wanted ") } func Test_HeaderContains_success(t *testing.T) { @@ -75,7 +85,9 @@ func Test_HeaderContains_failure(t *testing.T) { resp := newResponse(http.StatusOK, http.Header{"Content-Type": {"text/plain"}}, "") result := httpassert.Response(resp).HeaderContains("Content-Type", "application/json") - requireFailure(t, result, "got header =, wanted containing ") + spy := eye.Spy() + hammy.New(spy).Is(result) + spy.HadErrorContaining(t, "got header =, wanted containing ") } func Test_BodyEqual_success(t *testing.T) { @@ -89,7 +101,9 @@ func Test_BodyEqual_failure(t *testing.T) { resp := newResponse(http.StatusOK, nil, "hello world") result := httpassert.Response(resp).BodyEqual("goodbye") - requireFailure(t, result, "got body , wanted equal to ") + spy := eye.Spy() + hammy.New(spy).Is(result) + spy.HadErrorContaining(t, "got body , wanted equal to ") } func Test_BodyContains_success(t *testing.T) { @@ -103,7 +117,9 @@ func Test_BodyContains_failure(t *testing.T) { resp := newResponse(http.StatusOK, nil, "hello world") result := httpassert.Response(resp).BodyContains("goodbye") - requireFailure(t, result, "got body , wanted containing ") + spy := eye.Spy() + hammy.New(spy).Is(result) + spy.HadErrorContaining(t, "got body , wanted containing ") } func Test_BodyMatchesRegexp_success(t *testing.T) { @@ -117,14 +133,18 @@ func Test_BodyMatchesRegexp_failure(t *testing.T) { resp := newResponse(http.StatusOK, nil, "status 204") result := httpassert.Response(resp).BodyMatchesRegexp(`status 5\d\d`) - requireFailure(t, result, "got body , wanted regexp ") + spy := eye.Spy() + hammy.New(spy).Is(result) + spy.HadErrorContaining(t, "got body , wanted regexp ") } func Test_BodyMatchesRegexp_failure_invalid_pattern(t *testing.T) { resp := newResponse(http.StatusOK, nil, "status 204") result := httpassert.Response(resp).BodyMatchesRegexp(`(`) - requireFailure(t, result, "invalid regexp <(>") + spy := eye.Spy() + hammy.New(spy).Is(result) + spy.HadErrorContaining(t, "invalid regexp <(>") } func Test_Body_assertions_restore_body(t *testing.T) { @@ -139,14 +159,18 @@ func Test_Body_assertions_restore_body(t *testing.T) { func Test_Body_assertion_failure_nil_response(t *testing.T) { result := httpassert.Response(nil).BodyEqual("hello") - requireFailure(t, result, "got nil response") + spy := eye.Spy() + hammy.New(spy).Is(result) + spy.HadErrorContaining(t, "got nil response") } func Test_Body_assertion_failure_read_error(t *testing.T) { resp := &http.Response{Body: errorReadCloser{}} result := httpassert.Response(resp).BodyEqual("hello") - requireFailure(t, result, "got body read error: read failed") + spy := eye.Spy() + hammy.New(spy).Is(result) + spy.HadErrorContaining(t, "got body read error: read failed") } func newResponse(status int, headers http.Header, body string) *http.Response { @@ -161,16 +185,6 @@ func newResponse(status int, headers http.Header, body string) *http.Response { return recorder.Result() } -func requireFailure(t *testing.T, result hammy.AssertionMessage, contains string) { - t.Helper() - if result.IsSuccessful { - t.Fatalf("got success, wanted failure containing %q", contains) - } - if !strings.Contains(result.Message, contains) { - t.Fatalf("got message %q, wanted containing %q", result.Message, contains) - } -} - type errorReadCloser struct{} func (errorReadCloser) Read([]byte) (int, error) { @@ -187,5 +201,7 @@ func Test_Response_methods_failure_nil_receiver(t *testing.T) { var resp *httpassert.Resp result := resp.Status(http.StatusOK) - requireFailure(t, result, "got nil response") + spy := eye.Spy() + hammy.New(spy).Is(result) + spy.HadErrorContaining(t, "got nil response") }