Test if http.Response.Body is closed

Viewed 2175

I'm searching for an easy way to test that http.Response.Body.Close() was called.

I already tried this by counting the number of open connections to the server (https://golang.org/src/net/http/serve_test.go - countCloseListener), but it seems that it only is testable on the client side, via rewriting the Roundtripper.

So the following example works, but I'm wondering whether there is an easier way for such a common task...

package closing

import "net/http"

type MyClient struct {
    Client http.Client
}

func (c *MyClient) Closing() (err error) {
    res, err := c.Client.Get("http://localhost")
    defer res.Body.Close()
    return
}

func (c *MyClient) NotClosing() (err error) {
    _, err = c.Client.Get("http://localhost")
    return
}

https://github.com/elgohr/golang-test-body-close/blob/master/performsHttpRequest.go

package closing_test

import (
    "github.com/elgohr/closing"
    "io"
    "io/ioutil"
    "net/http"
    "strings"
    "testing"
)

func TestShouldBeClosedWhenClosed(t *testing.T) {
    tripper := NewFakeRoundTripper()
    cl := closing.MyClient{
        Client: http.Client{
            Transport: tripper,
        },
    }

    if err := cl.Closing(); err != nil {
        t.Error(err)
    }

    if !tripper.Body.Closed {
        t.Error("Should be closed, but wasn't")
    }
}

func TestShouldBeOpenWhenNotClosed(t *testing.T) {
    tripper := NewFakeRoundTripper()
    cl := closing.MyClient{
        Client: http.Client{
            Transport: tripper,
        },
    }

    if err := cl.NotClosing(); err != nil {
        t.Error(err)
    }

    if tripper.Body.Closed {
        t.Error("Should be open, but wasn't")
    }
}

func NewFakeRoundTripper() *FakeRoundTripper {
    return &FakeRoundTripper{
        Body: &FakeReadCloser{
            ReadCloser: ioutil.NopCloser(strings.NewReader("content")),
        },
    }
}

type FakeRoundTripper struct {
    Body *FakeReadCloser
}

func (r *FakeRoundTripper) RoundTrip(*http.Request) (*http.Response, error) {
    return &http.Response{
        Body: r.Body,
    }, nil
}

type FakeReadCloser struct {
    io.ReadCloser
    Closed bool
}

func (r *FakeReadCloser) Close() error {
    r.Closed = true
    return r.ReadCloser.Close()
}

https://github.com/elgohr/golang-test-body-close/blob/master/performsHttpRequest_test.go

1 Answers

Try to read from the body to test if it was closed.

r, err := http.Get("http://google.com")
r.Body.Close()

_, err = r.Body.Read(nil)
if err == nil {
  // can still read from body so it's not closed

} else if err != nil && err.Error() == "http: read on closed response body" {
  // body is already closed
} 

Unfortunately the returned error errReadOnClosedResBody is not exported, so you'd have to compare the string value.

Related