Repository: stretchr/testify (go)

Issue:
Adding HTTPStatusCodeEqual to assert if status code equals to expected value
Currently we have [`HTTPError`](https://godoc.org/github.com/stretchr/testify/assert#HTTPError) which check if the http response contains status code of 400 and above. I am wordering if we can add another function to check for specific status code like something below:

```go
func HTTPStatusCodeEqual(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statusCode int, msgAndArgs ...interface{}) bool
```

I can send a PR if you think this is something that can be included in the package. Thank you.


Pull request:
Add HTTPStatusCode assertion
@glesica @boyan-soubachov  Please consider this PR for merging.

**Motivation**
The current set of HTTP asserts do not cover the full range of status codes. This addition would allow a user to test for a specific status code.

As such it would cover the full range of status codes, including for example: `http.StatusSwitchingProtocols`.

**Example usage**
`assert.HTTPStatusCode(t, myHandler, "GET", "/", nil, http.StatusSwitchingProtocols)`

**Whether you intend to add / change behaviour or fix a bug**
Adds behaviour.

**Additionally**
- Would also close #824 
- Includes test
- Tests ran successfully

Patch:
diff --git a/assert/assertion_format.go b/assert/assertion_format.go
index bf89ecd..c07387d 100644
--- a/assert/assertion_format.go
+++ b/assert/assertion_format.go
@@ -245,6 +245,18 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri
 	return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
 }
 
+// HTTPStatusCodef asserts that a specified handler returns a specified status code.
+//
+//  assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool {
+	if h, ok := t.(tHelper); ok {
+		h.Helper()
+	}
+	return HTTPStatusCode(t, handler, method, url, values, statuscode, append([]interface{}{msg}, args...)...)
+}
+
 // HTTPSuccessf asserts that a specified handler returns a success status code.
 //
 //  assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
diff --git a/assert/assertion_forward.go b/assert/assertion_forward.go
index 75ecdca..d55c980 100644
--- a/assert/assertion_forward.go
+++ b/assert/assertion_forward.go
@@ -479,6 +479,30 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url
 	return HTTPRedirectf(a.t, handler, method, url, values, msg, args...)
 }
 
+// HTTPStatusCode asserts that a specified handler returns a specified status code.
+//
+//  a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501)
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool {
+	if h, ok := a.t.(tHelper); ok {
+		h.Helper()
+	}
+	return HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...)
+}
+
+// HTTPStatusCodef asserts that a specified handler returns a specified status code.
+//
+//  a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool {
+	if h, ok := a.t.(tHelper); ok {
+		h.Helper()
+	}
+	return HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...)
+}
+
 // HTTPSuccess asserts that a specified handler returns a success status code.
 //
 //  a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil)
diff --git a/assert/http_assertions.go b/assert/http_assertions.go
index df46fa7..4ed341d 100644
--- a/assert/http_assertions.go
+++ b/assert/http_assertions.go
@@ -33,7 +33,6 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value
 	code, err := httpCode(handler, method, url, values)
 	if err != nil {
 		Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
-		return false
 	}
 
 	isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent
@@ -56,7 +55,6 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu
 	code, err := httpCode(handler, method, url, values)
 	if err != nil {
 		Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
-		return false
 	}
 
 	isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect
@@ -79,7 +77,6 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values
 	code, err := httpCode(handler, method, url, values)
 	if err != nil {
 		Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
-		return false
 	}
 
 	isErrorCode := code >= http.StatusBadRequest
@@ -90,6 +87,28 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values
 	return isErrorCode
 }
 
+// HTTPStatusCode asserts that a specified handler returns a specified status code.
+//
+//  assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501)
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool {
+	if h, ok := t.(tHelper); ok {
+		h.Helper()
+	}
+	code, err := httpCode(handler, method, url, values)
+	if err != nil {
+		Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
+	}
+
+	successful := code == statuscode
+	if !successful {
+		Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code))
+	}
+
+	return successful
+}
+
 // HTTPBody is a helper that returns HTTP body of the response. It returns
 // empty string if building a new request fails.
 func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string {
diff --git a/require/require.go b/require/require.go
index cf6c7b5..22784c1 100644
--- a/require/require.go
+++ b/require/require.go
@@ -606,6 +606,36 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri
 	t.FailNow()
 }
 
+// HTTPStatusCode asserts that a specified handler returns a specified status code.
+//
+//  assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501)
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) {
+	if h, ok := t.(tHelper); ok {
+		h.Helper()
+	}
+	if assert.HTTPStatusCode(t, handler, method, url, values, statuscode, msgAndArgs...) {
+		return
+	}
+	t.FailNow()
+}
+
+// HTTPStatusCodef asserts that a specified handler returns a specified status code.
+//
+//  assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) {
+	if h, ok := t.(tHelper); ok {
+		h.Helper()
+	}
+	if assert.HTTPStatusCodef(t, handler, method, url, values, statuscode, msg, args...) {
+		return
+	}
+	t.FailNow()
+}
+
 // HTTPSuccess asserts that a specified handler returns a success status code.
 //
 //  assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
diff --git a/require/require_forward.go b/require/require_forward.go
index 5aac226..b556d5d 100644
--- a/require/require_forward.go
+++ b/require/require_forward.go
@@ -480,6 +480,30 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url
 	HTTPRedirectf(a.t, handler, method, url, values, msg, args...)
 }
 
+// HTTPStatusCode asserts that a specified handler returns a specified status code.
+//
+//  a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501)
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) {
+	if h, ok := a.t.(tHelper); ok {
+		h.Helper()
+	}
+	HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...)
+}
+
+// HTTPStatusCodef asserts that a specified handler returns a specified status code.
+//
+//  a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) {
+	if h, ok := a.t.(tHelper); ok {
+		h.Helper()
+	}
+	HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...)
+}
+
 // HTTPSuccess asserts that a specified handler returns a success status code.
 //
 //  a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil)


Test patch:
diff --git a/assert/http_assertions_test.go b/assert/http_assertions_test.go
index 112beaf..991866a 100644
--- a/assert/http_assertions_test.go
+++ b/assert/http_assertions_test.go
@@ -19,6 +19,10 @@ func httpError(w http.ResponseWriter, r *http.Request) {
 	w.WriteHeader(http.StatusInternalServerError)
 }
 
+func httpStatusCode(w http.ResponseWriter, r *http.Request) {
+	w.WriteHeader(http.StatusSwitchingProtocols)
+}
+
 func TestHTTPSuccess(t *testing.T) {
 	assert := New(t)
 
@@ -33,6 +37,10 @@ func TestHTTPSuccess(t *testing.T) {
 	mockT3 := new(testing.T)
 	assert.Equal(HTTPSuccess(mockT3, httpError, "GET", "/", nil), false)
 	assert.True(mockT3.Failed())
+
+	mockT4 := new(testing.T)
+	assert.Equal(HTTPSuccess(mockT4, httpStatusCode, "GET", "/", nil), false)
+	assert.True(mockT4.Failed())
 }
 
 func TestHTTPRedirect(t *testing.T) {
@@ -49,6 +57,10 @@ func TestHTTPRedirect(t *testing.T) {
 	mockT3 := new(testing.T)
 	assert.Equal(HTTPRedirect(mockT3, httpError, "GET", "/", nil), false)
 	assert.True(mockT3.Failed())
+
+	mockT4 := new(testing.T)
+	assert.Equal(HTTPRedirect(mockT4, httpStatusCode, "GET", "/", nil), false)
+	assert.True(mockT4.Failed())
 }
 
 func TestHTTPError(t *testing.T) {
@@ -65,6 +77,30 @@ func TestHTTPError(t *testing.T) {
 	mockT3 := new(testing.T)
 	assert.Equal(HTTPError(mockT3, httpError, "GET", "/", nil), true)
 	assert.False(mockT3.Failed())
+
+	mockT4 := new(testing.T)
+	assert.Equal(HTTPError(mockT4, httpStatusCode, "GET", "/", nil), false)
+	assert.True(mockT4.Failed())
+}
+
+func TestHTTPStatusCode(t *testing.T) {
+	assert := New(t)
+
+	mockT1 := new(testing.T)
+	assert.Equal(HTTPStatusCode(mockT1, httpOK, "GET", "/", nil, http.StatusSwitchingProtocols), false)
+	assert.True(mockT1.Failed())
+
+	mockT2 := new(testing.T)
+	assert.Equal(HTTPStatusCode(mockT2, httpRedirect, "GET", "/", nil, http.StatusSwitchingProtocols), false)
+	assert.True(mockT2.Failed())
+
+	mockT3 := new(testing.T)
+	assert.Equal(HTTPStatusCode(mockT3, httpError, "GET", "/", nil, http.StatusSwitchingProtocols), false)
+	assert.True(mockT3.Failed())
+
+	mockT4 := new(testing.T)
+	assert.Equal(HTTPStatusCode(mockT4, httpStatusCode, "GET", "/", nil, http.StatusSwitchingProtocols), true)
+	assert.False(mockT4.Failed())
 }
 
 func TestHTTPStatusesWrapper(t *testing.T) {