Documentation

Overview

Package tdhttp provides some functions to easily test HTTP handlers.

Combined to td package (https://pkg.go.dev/github.com/maxatome/go-testdeep/td) it provides powerful testing features.

TestAPI

The better way to test HTTP APIs using this package.

ta := tdhttp.NewTestAPI(t, mux)

ta.Get("/person/42", "Accept", "application/xml").
  CmpStatus(http.StatusOK).
  CmpHeader(td.ContainsKey("X-Custom-Header")).
  CmpXMLBody(Person{
    ID:   ta.Anchor(td.NotZero(), uint64(0)).(uint64),
    Name: "Bob",
    Age:  26,
  })

ta.Get("/person/42", "Accept", "application/json").
  CmpStatus(http.StatusOK).
  CmpHeader(td.ContainsKey("X-Custom-Header")).
  CmpJSONBody(td.JSON(`
{
  "id":   $1,
  "name": "Bob",
  "age":  26
}`,
    td.NotZero()))

See the full example below.

Cmp…Response functions

Historically, it was the only way to test HTTP APIs using this package.

ok := tdhttp.CmpJSONResponse(t,
  tdhttp.Get("/person/42"),
  myAPI.ServeHTTP,
  Response{
    Status: http.StatusOK,
    Header: td.ContainsKey("X-Custom-Header"),
    Body:   Person{
      ID:   42,
      Name: "Bob",
      Age:  26,
    },
  },
  "/person/{id} route")

It now uses TestAPI behind the scene. It is better to directly use TestAPI and its methods instead, as it is more flexible and readable.

Example
Output:

GET /person/42 - JSON: true
GET /person/42 - XML: true
GET /person/42 - raw: true
POST /person - XML: true → Bob ID=1
POST /person - JSON: true → Alice ID=2
POST /person - raw: true → Britt ID=3
GET XML Alice: true
GET JSON Alice: true
DELETE Alice: true
Alice is not found anymore: true

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func CmpJSONResponse

func CmpJSONResponse(t testing.TB,
	req *http.Request,
	handler func(w http.ResponseWriter, r *http.Request),
	expectedResp Response,
	args ...interface{},
) bool

    CmpJSONResponse is used to match a JSON response body. The req *http.Request is launched against handler. If expectedResp.Body is non-nil, the response body is json.Unmarshal'ed. The response is then tested against expectedResp.

    "args..." are optional and allow to name the test, a t.Log() done before starting any test. If len(args) > 1 and the first item of "args" is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else "args" are passed to fmt.Fprint.

    It returns true if the tests succeed, false otherwise.

    ok := tdhttp.CmpJSONResponse(t,
      tdhttp.Get("/person/42"),
      myAPI.ServeHTTP,
      Response{
        Status: http.StatusOK,
        Header: td.ContainsKey("X-Custom-Header"),
        Body:   Person{
          ID:   42,
          Name: "Bob",
          Age:  26,
        },
      },
      "/person/{id} route")
    

    Status, Header and Body fields of Response struct can all be TestDeep operators as it is for Header field here. Otherwise, Status should be an int, Header a http.Header and Body any type encoding/json can Unmarshal into.

    If Status and Header are omitted (or nil), they are not tested.

    If Body is omitted (or nil), it means the body response has to be empty. If you want to ignore the body response, use td.Ignore() explicitly.

    See TestAPI type and its methods for more flexible tests.

    func CmpJSONResponseFunc

    func CmpJSONResponseFunc(req *http.Request,
    	handler func(w http.ResponseWriter, r *http.Request),
    	expectedResp Response) func(t *testing.T)

      CmpJSONResponseFunc returns a function ready to be used with testing.Run, calling CmpJSONResponse behind the scene. As it is intended to be used in conjunction with testing.Run() which names the sub-test, the test name part (args...) is voluntary omitted.

      t.Run("Subtest name", tdhttp.CmpJSONResponseFunc(
        tdhttp.Get("/json"),
        mux.ServeHTTP,
        tdhttp.Response{
          Status: http.StatusOK,
          Body:   JResp{Comment: "expected comment!"},
        }))
      

      See CmpJSONResponse documentation for details.

      See TestAPI type and its methods for more flexible tests.

      func CmpMarshaledResponse

      func CmpMarshaledResponse(t testing.TB,
      	req *http.Request,
      	handler func(w http.ResponseWriter, r *http.Request),
      	unmarshal func([]byte, interface{}) error,
      	expectedResp Response,
      	args ...interface{},
      ) bool

        CmpMarshaledResponse is the base function used by some others in tdhttp package. The req *http.Request is launched against handler. The response body is unmarshaled using unmarshal. The response is then tested against expectedResp.

        "args..." are optional and allow to name the test, a t.Log() done before starting any test. If len(args) > 1 and the first item of "args" is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else "args" are passed to fmt.Fprint.

        It returns true if the tests succeed, false otherwise.

        See TestAPI type and its methods for more flexible tests.

        func CmpMarshaledResponseFunc

        func CmpMarshaledResponseFunc(req *http.Request,
        	handler func(w http.ResponseWriter, r *http.Request),
        	unmarshal func([]byte, interface{}) error,
        	expectedResp Response) func(t *testing.T)

          CmpMarshaledResponseFunc returns a function ready to be used with testing.Run, calling CmpMarshaledResponse behind the scene. As it is intended to be used in conjunction with testing.Run() which names the sub-test, the test name part (args...) is voluntary omitted.

          t.Run("Subtest name", tdhttp.CmpMarshaledResponseFunc(
            tdhttp.Get("/text"),
            mux.ServeHTTP,
            tdhttp.Response{
              Status: http.StatusOK,
            }))
          

          See CmpMarshaledResponse documentation for details.

          See TestAPI type and its methods for more flexible tests.

          func CmpResponse

          func CmpResponse(t testing.TB,
          	req *http.Request,
          	handler func(w http.ResponseWriter, r *http.Request),
          	expectedResp Response,
          	args ...interface{}) bool

            CmpResponse is used to match a []byte or string response body. The req *http.Request is launched against handler. If expectedResp.Body is non-nil, the response body is converted to []byte or string, depending on the expectedResp.Body type. The response is then tested against expectedResp.

            "args..." are optional and allow to name the test, a t.Log() done before starting any test. If len(args) > 1 and the first item of "args" is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else "args" are passed to fmt.Fprint.

            It returns true if the tests succeed, false otherwise.

            ok := tdhttp.CmpResponse(t,
              tdhttp.Get("/test"),
              myAPI.ServeHTTP,
              Response{
                Status: http.StatusOK,
                Header: td.ContainsKey("X-Custom-Header"),
                Body:   "OK!\n",
              },
              "/test route")
            

            Status, Header and Body fields of Response struct can all be TestDeep operators as it is for Header field here. Otherwise, Status should be an int, Header a http.Header and Body a []byte or a string.

            See TestAPI type and its methods for more flexible tests.

            func CmpResponseFunc

            func CmpResponseFunc(req *http.Request,
            	handler func(w http.ResponseWriter, r *http.Request),
            	expectedResp Response) func(t *testing.T)

              CmpResponseFunc returns a function ready to be used with testing.Run, calling CmpResponse behind the scene. As it is intended to be used in conjunction with testing.Run() which names the sub-test, the test name part (args...) is voluntary omitted.

              t.Run("Subtest name", tdhttp.CmpResponseFunc(
                tdhttp.Get("/text"),
                mux.ServeHTTP,
                tdhttp.Response{
                  Status: http.StatusOK,
                }))
              

              See CmpResponse documentation for details.

              See TestAPI type and its methods for more flexible tests.

              func CmpXMLResponse

              func CmpXMLResponse(t testing.TB,
              	req *http.Request,
              	handler func(w http.ResponseWriter, r *http.Request),
              	expectedResp Response,
              	args ...interface{},
              ) bool

                CmpXMLResponse is used to match an XML response body. The req *http.Request is launched against handler. If expectedResp.Body is non-nil, the response body is xml.Unmarshal'ed. The response is then tested against expectedResp.

                "args..." are optional and allow to name the test, a t.Log() done before starting any test. If len(args) > 1 and the first item of "args" is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else "args" are passed to fmt.Fprint.

                It returns true if the tests succeed, false otherwise.

                ok := tdhttp.CmpXMLResponse(t,
                  tdhttp.Get("/person/42"),
                  myAPI.ServeHTTP,
                  Response{
                    Status: http.StatusOK,
                    Header: td.ContainsKey("X-Custom-Header"),
                    Body:   Person{
                      ID:   42,
                      Name: "Bob",
                      Age:  26,
                    },
                  },
                  "/person/{id} route")
                

                Status, Header and Body fields of Response struct can all be TestDeep operators as it is for Header field here. Otherwise, Status should be an int, Header a http.Header and Body any type encoding/xml can Unmarshal into.

                If Status and Header are omitted (or nil), they are not tested.

                If Body is omitted (or nil), it means the body response has to be empty. If you want to ignore the body response, use td.Ignore() explicitly.

                See TestAPI type and its methods for more flexible tests.

                func CmpXMLResponseFunc

                func CmpXMLResponseFunc(req *http.Request,
                	handler func(w http.ResponseWriter, r *http.Request),
                	expectedResp Response) func(t *testing.T)

                  CmpXMLResponseFunc returns a function ready to be used with testing.Run, calling CmpXMLResponse behind the scene. As it is intended to be used in conjunction with testing.Run() which names the sub-test, the test name part (args...) is voluntary omitted.

                  t.Run("Subtest name", tdhttp.CmpXMLResponseFunc(
                    tdhttp.Get("/xml"),
                    mux.ServeHTTP,
                    tdhttp.Response{
                      Status: http.StatusOK,
                      Body:   JResp{Comment: "expected comment!"},
                    }))
                  

                  See CmpXMLResponse documentation for details.

                  See TestAPI type and its methods for more flexible tests.

                  func Delete

                  func Delete(target string, body io.Reader, headers ...interface{}) *http.Request

                    Delete creates a HTTP DELETE. It is a shortcut for:

                    NewRequest(http.MethodDelete, target, body, headers...)
                    

                    See NewRequest for all possible formats accepted in headers.

                    func DeleteJSON

                    func DeleteJSON(target string, body interface{}, headers ...interface{}) *http.Request

                      DeleteJSON creates a HTTP DELETE with body marshaled to JSON. "Content-Type" header is automatically set to "application/json". It is a shortcut for:

                      NewJSONRequest(http.MethodDelete, target, body, headers...)
                      

                      See NewRequest for all possible formats accepted in headers.

                      func DeleteXML

                      func DeleteXML(target string, body interface{}, headers ...interface{}) *http.Request

                        DeleteXML creates a HTTP DELETE with body marshaled to XML. "Content-Type" header is automatically set to "application/xml". It is a shortcut for:

                        NewXMLRequest(http.MethodDelete, target, body, headers...)
                        

                        See NewRequest for all possible formats accepted in headers.

                        func Get

                        func Get(target string, headers ...interface{}) *http.Request

                          Get creates a new HTTP GET. It is a shortcut for:

                          NewRequest(http.MethodGet, target, nil, headers...)
                          

                          See NewRequest for all possible formats accepted in headers.

                          func Head(target string, headers ...interface{}) *http.Request

                            Head creates a new HTTP HEAD. It is a shortcut for:

                            NewRequest(http.MethodHead, target, nil, headers...)
                            

                            See NewRequest for all possible formats accepted in headers.

                            func NewJSONRequest

                            func NewJSONRequest(method, target string, body interface{}, headers ...interface{}) *http.Request

                              NewJSONRequest creates a new HTTP request with body marshaled to JSON. "Content-Type" header is automatically set to "application/json". Other headers can be added via headers, as in:

                              req := NewJSONRequest("POST", "/data", body,
                                "X-Foo", "Foo-value",
                                "X-Zip", "Zip-value",
                              )
                              

                              See NewRequest for all possible formats accepted in headers.

                              func NewRequest

                              func NewRequest(method, target string, body io.Reader, headers ...interface{}) *http.Request

                                NewRequest creates a new HTTP request as net/http/httptest.NewRequest does, with the ability to immediately add some headers using string pairs as in:

                                req := NewRequest("POST", "/pdf", body,
                                  "Content-type", "application/pdf",
                                  "X-Test", "value",
                                )
                                

                                or using http.Header as in:

                                req := NewRequest("POST", "/pdf", body,
                                  http.Header{"Content-type": []string{"application/pdf"}},
                                )
                                

                                Several header sources are combined:

                                req := NewRequest("POST", "/pdf", body,
                                  "Content-type", "application/pdf",
                                  http.Header{"X-Test": []string{"value1"}},
                                  "X-Test", "value2",
                                )
                                

                                Produce the following http.Header:

                                http.Header{
                                  "Content-type": []string{"application/pdf"},
                                  "X-Test":       []string{"value1", "value2"},
                                }
                                

                                A string slice or a map can be flatened as well. As NewRequest() expects ...interface{}, td.Flatten() can help here too:

                                strHeaders := map[string]string{
                                  "X-Length": "666",
                                  "X-Foo":    "bar",
                                }
                                req := NewRequest("POST", "/pdf", body, td.Flatten(strHeaders))
                                

                                Or combined with forms seen above:

                                req := NewRequest("POST", "/pdf",
                                  "Content-type", "application/pdf",
                                  http.Header{"X-Test": []string{"value1"}},
                                  td.Flatten(strHeaders),
                                  "X-Test", "value2",
                                )
                                

                                func NewXMLRequest

                                func NewXMLRequest(method, target string, body interface{}, headers ...interface{}) *http.Request

                                  NewXMLRequest creates a new HTTP request with body marshaled to XML. "Content-Type" header is automatically set to "application/xml". Other headers can be added via headers, as in:

                                  req := NewXMLRequest("POST", "/data", body,
                                    "X-Foo", "Foo-value",
                                    "X-Zip", "Zip-value",
                                  )
                                  

                                  See NewRequest for all possible formats accepted in headers.

                                  func Patch

                                  func Patch(target string, body io.Reader, headers ...interface{}) *http.Request

                                    Patch creates a HTTP PATCH. It is a shortcut for:

                                    NewRequest(http.MethodPatch, target, body, headers...)
                                    

                                    See NewRequest for all possible formats accepted in headers.

                                    func PatchJSON

                                    func PatchJSON(target string, body interface{}, headers ...interface{}) *http.Request

                                      PatchJSON creates a HTTP PATCH with body marshaled to JSON. "Content-Type" header is automatically set to "application/json". It is a shortcut for:

                                      NewJSONRequest(http.MethodPatch, target, body, headers...)
                                      

                                      See NewRequest for all possible formats accepted in headers.

                                      func PatchXML

                                      func PatchXML(target string, body interface{}, headers ...interface{}) *http.Request

                                        PatchXML creates a HTTP PATCH with body marshaled to XML. "Content-Type" header is automatically set to "application/xml". It is a shortcut for:

                                        NewXMLRequest(http.MethodPatch, target, body, headers...)
                                        

                                        See NewRequest for all possible formats accepted in headers.

                                        func Post

                                        func Post(target string, body io.Reader, headers ...interface{}) *http.Request

                                          Post creates a HTTP POST. It is a shortcut for:

                                          NewRequest(http.MethodPost, target, body, headers...)
                                          

                                          See NewRequest for all possible formats accepted in headers.

                                          func PostForm

                                          func PostForm(target string, data url.Values, headers ...interface{}) *http.Request

                                            PostForm creates a HTTP POST with data's keys and values URL-encoded as the request body. "Content-Type" header is automatically set to "application/x-www-form-urlencoded". Other headers can be added via headers, as in:

                                            req := PostForm("/data",
                                              url.Values{
                                                "param1": []string{"val1", "val2"},
                                                "param2": []string{"zip"},
                                              },
                                              "X-Foo", "Foo-value",
                                              "X-Zip", "Zip-value",
                                            )
                                            

                                            See NewRequest for all possible formats accepted in headers.

                                            func PostJSON

                                            func PostJSON(target string, body interface{}, headers ...interface{}) *http.Request

                                              PostJSON creates a HTTP POST with body marshaled to JSON. "Content-Type" header is automatically set to "application/json". It is a shortcut for:

                                              NewJSONRequest(http.MethodPost, target, body, headers...)
                                              

                                              See NewRequest for all possible formats accepted in headers.

                                              func PostXML

                                              func PostXML(target string, body interface{}, headers ...interface{}) *http.Request

                                                PostXML creates a HTTP POST with body marshaled to XML. "Content-Type" header is automatically set to "application/xml". It is a shortcut for:

                                                NewXMLRequest(http.MethodPost, target, body, headers...)
                                                

                                                See NewRequest for all possible formats accepted in headers.

                                                func Put

                                                func Put(target string, body io.Reader, headers ...interface{}) *http.Request

                                                  Put creates a HTTP PUT. It is a shortcut for:

                                                  NewRequest(http.MethodPut, target, body, headers...)
                                                  

                                                  See NewRequest for all possible formats accepted in headers.

                                                  func PutJSON

                                                  func PutJSON(target string, body interface{}, headers ...interface{}) *http.Request

                                                    PutJSON creates a HTTP PUT with body marshaled to JSON. "Content-Type" header is automatically set to "application/json". It is a shortcut for:

                                                    NewJSONRequest(http.MethodPut, target, body, headers...)
                                                    

                                                    See NewRequest for all possible formats accepted in headers.

                                                    func PutXML

                                                    func PutXML(target string, body interface{}, headers ...interface{}) *http.Request

                                                      PutXML creates a HTTP PUT with body marshaled to XML. "Content-Type" header is automatically set to "application/xml". It is a shortcut for:

                                                      NewXMLRequest(http.MethodPut, target, body, headers...)
                                                      

                                                      See NewRequest for all possible formats accepted in headers.

                                                      Types

                                                      type Response

                                                      type Response struct {
                                                      	Status interface{} // Status is the expected status (ignored if nil)
                                                      	Header interface{} // Header is the expected header (ignored if nil)
                                                      	Body   interface{} // Body is the expected body (expected to be empty if nil)
                                                      }

                                                        Response is used by Cmp*Response functions to make the HTTP response match easier. Each field, can be a TestDeep operator as well as the exact expected value.

                                                        type TestAPI

                                                        type TestAPI struct {
                                                        	// contains filtered or unexported fields
                                                        }

                                                          TestAPI allows to test one HTTP API. See NewTestAPI function to create a new instance and get some examples of use.

                                                          func NewTestAPI

                                                          func NewTestAPI(tb testing.TB, handler http.Handler) *TestAPI

                                                            NewTestAPI creates a TestAPI that can be used to test routes of the API behind "handler".

                                                            tdhttp.NewTestAPI(t, mux).
                                                              Get("/test").
                                                              CmpStatus(200).
                                                              CmpBody("OK!")
                                                            

                                                            Several routes can be tested with the same instance as in:

                                                            ta := tdhttp.NewTestAPI(t, mux)
                                                            
                                                            ta.Get("/test").
                                                              CmpStatus(200).
                                                              CmpBody("OK!")
                                                            
                                                            ta.Get("/ping").
                                                              CmpStatus(200).
                                                              CmpBody("pong")
                                                            

                                                            Note that "tb" can be a *testing.T as well as a *td.T.

                                                            func (*TestAPI) A

                                                            func (t *TestAPI) A(operator td.TestDeep, model ...interface{}) interface{}

                                                              A is a synonym for Anchor. It returns a typed value allowing to anchor the TestDeep operator "operator" in a go classic litteral like a struct, slice, array or map value.

                                                              ta := tdhttp.NewTestAPI(tt, mux)
                                                              
                                                              ta.Get("/person/42").
                                                                CmpStatus(http.StatusOK).
                                                                CmpJSONBody(Person{
                                                                  ID:   ta.A(td.NotZero(), uint64(0)).(uint64),
                                                                  Name: "Bob",
                                                                  Age:  26,
                                                                })
                                                              

                                                              See (*td.T).Anchor documentation https://pkg.go.dev/github.com/maxatome/go-testdeep/td#T.Anchor for details.

                                                              func (*TestAPI) Anchor

                                                              func (t *TestAPI) Anchor(operator td.TestDeep, model ...interface{}) interface{}

                                                                Anchor returns a typed value allowing to anchor the TestDeep operator "operator" in a go classic litteral like a struct, slice, array or map value.

                                                                ta := tdhttp.NewTestAPI(tt, mux)
                                                                
                                                                ta.Get("/person/42").
                                                                  CmpStatus(http.StatusOK).
                                                                  CmpJSONBody(Person{
                                                                    ID:   ta.Anchor(td.NotZero(), uint64(0)).(uint64),
                                                                    Name: "Bob",
                                                                    Age:  26,
                                                                  })
                                                                

                                                                See (*td.T).Anchor documentation for details https://pkg.go.dev/github.com/maxatome/go-testdeep/td#T.Anchor

                                                                See A method for a shorter synonym of Anchor.

                                                                func (*TestAPI) AutoDumpResponse

                                                                func (t *TestAPI) AutoDumpResponse() *TestAPI

                                                                  AutoDumpResponse allows to dump the HTTP response when the first error is encountered after a request.

                                                                  func (*TestAPI) CmpBody

                                                                  func (t *TestAPI) CmpBody(expectedBody interface{}) *TestAPI

                                                                    CmpBody tests the last request response body against expectedBody. expectedBody can be a []byte, a string or a TestDeep operator.

                                                                    ta := tdhttp.NewTestAPI(t, mux)
                                                                    
                                                                    ta.Get("/test").
                                                                      CmpStatus(http.StatusOK).
                                                                      CmpBody("OK!\n")
                                                                    
                                                                    ta.Get("/test").
                                                                      CmpStatus(http.StatusOK).
                                                                      CmpBody(td.Contains("OK"))
                                                                    

                                                                    It fails if no request has been sent yet.

                                                                    func (*TestAPI) CmpHeader

                                                                    func (t *TestAPI) CmpHeader(expectedHeader interface{}) *TestAPI

                                                                      CmpHeader tests the last request response header against expectedHeader. expectedHeader can be a http.Header or a TestDeep operator. Keep in mind that if it is a http.Header, it has to match exactly the response header. Often only the presence of a header key is needed:

                                                                      ta := tdhttp.NewTestAPI(t, mux).
                                                                        PostJSON("/new", map[string]string{"name": "Bob"}).
                                                                        CmdStatus(201).
                                                                        CmpHeader(td.ContainsKey("X-Custom"))
                                                                      

                                                                      or some specific key, value pairs:

                                                                      ta.CmpHeader(td.SuperMapOf(
                                                                        http.Header{
                                                                          "X-Account": []string{"Bob"},
                                                                        },
                                                                        td.MapEntries{
                                                                          "X-Token": td.Re(`^[a-z0-9-]{32}\z`),
                                                                        }),
                                                                      )
                                                                      

                                                                      Note that CmpHeader calls can be chained:

                                                                      ta.CmpHeader(td.ContainsKey("X-Account")).
                                                                        CmpHeader(td.ContainsKey("X-Token"))
                                                                      

                                                                      instead of doing all tests in one call as All operator allows it:

                                                                      ta.CmpHeader(td.All(
                                                                        td.ContainsKey("X-Account"),
                                                                        td.ContainsKey("X-Token")),
                                                                      )
                                                                      

                                                                      It fails if no request has been sent yet.

                                                                      func (*TestAPI) CmpJSONBody

                                                                      func (t *TestAPI) CmpJSONBody(expectedBody interface{}) *TestAPI

                                                                        CmpJSONBody tests that the last request response body can be encoding/json.Unmarshall'ed and that it matches expectedBody. expectedBody can be any type encoding/json can Unmarshal into, or a TestDeep operator.

                                                                        ta := tdhttp.NewTestAPI(t, mux)
                                                                        
                                                                        ta.Get("/person/42").
                                                                          CmpStatus(http.StatusOK).
                                                                          CmpJSONBody(Person{
                                                                            ID:   42,
                                                                            Name: "Bob",
                                                                            Age:  26,
                                                                          })
                                                                        
                                                                        ta.PostJSON("/person", Person{Name: "Bob", Age: 23}).
                                                                          CmpStatus(http.StatusCreated).
                                                                          CmpJSONBody(td.SStruct(
                                                                            Person{
                                                                              Name: "Bob",
                                                                              Age:  26,
                                                                            },
                                                                            td.StructFields{
                                                                              "ID": td.NotZero(),
                                                                            }))
                                                                        

                                                                        The same with anchoring, and so without td.SStruct():

                                                                        ta := tdhttp.NewTestAPI(tt, mux)
                                                                        
                                                                        ta.PostJSON("/person", Person{Name: "Bob", Age: 23}).
                                                                          CmpStatus(http.StatusCreated).
                                                                          CmpJSONBody(Person{
                                                                            ID:   ta.Anchor(td.NotZero(), uint64(0)).(uint64),
                                                                            Name: "Bob",
                                                                            Age:  26,
                                                                          })
                                                                        

                                                                        The same using td.JSON():

                                                                        ta.PostJSON("/person", Person{Name: "Bob", Age: 23}).
                                                                          CmpStatus(http.StatusCreated).
                                                                          CmpJSONBody(td.JSON(`
                                                                        {
                                                                          "id":   NotZero(),
                                                                          "name": "Bob",
                                                                          "age":  26
                                                                        }`))
                                                                        

                                                                        It fails if no request has been sent yet.

                                                                        func (*TestAPI) CmpMarshaledBody

                                                                        func (t *TestAPI) CmpMarshaledBody(unmarshal func([]byte, interface{}) error, expectedBody interface{}) *TestAPI

                                                                          CmpMarshaledBody tests that the last request response body can be unmarshalled using unmarhsal function and then, that it matches expectedBody. expectedBody can be any type unmarshal function can handle, or a TestDeep operator.

                                                                          See CmpJSONBody and CmpXMLBody sources for examples of use.

                                                                          It fails if no request has been sent yet.

                                                                          func (*TestAPI) CmpStatus

                                                                          func (t *TestAPI) CmpStatus(expectedStatus interface{}) *TestAPI

                                                                            CmpStatus tests the last request response status against expectedStatus. expectedStatus can be an int to match a fixed HTTP status code, or a TestDeep operator.

                                                                            ta := tdhttp.NewTestAPI(t, mux)
                                                                            
                                                                            ta.Get("/test").
                                                                              CmpStatus(http.StatusOK)
                                                                            
                                                                            ta.PostJSON("/new", map[string]string{"name": "Bob"}).
                                                                              CmpStatus(td.Between(200, 202))
                                                                            

                                                                            It fails if no request has been sent yet.

                                                                            func (*TestAPI) CmpXMLBody

                                                                            func (t *TestAPI) CmpXMLBody(expectedBody interface{}) *TestAPI

                                                                              CmpXMLBody tests that the last request response body can be encoding/xml.Unmarshall'ed and that it matches expectedBody. expectedBody can be any type encoding/xml can Unmarshal into, or a TestDeep operator.

                                                                              ta := tdhttp.NewTestAPI(t, mux)
                                                                              
                                                                              ta.Get("/person/42").
                                                                                CmpStatus(http.StatusOK).
                                                                                CmpXMLBody(Person{
                                                                                  ID:   42,
                                                                                  Name: "Bob",
                                                                                  Age:  26,
                                                                                })
                                                                              
                                                                              ta.Get("/person/43").
                                                                                CmpStatus(http.StatusOK).
                                                                                CmpXMLBody(td.SStruct(
                                                                                  Person{
                                                                                    Name: "Bob",
                                                                                    Age:  26,
                                                                                  },
                                                                                  td.StructFields{
                                                                                    "ID": td.NotZero(),
                                                                                  }))
                                                                              

                                                                              The same with anchoring:

                                                                              ta := tdhttp.NewTestAPI(tt, mux)
                                                                              
                                                                              ta.Get("/person/42").
                                                                                CmpStatus(http.StatusOK).
                                                                                CmpXMLBody(Person{
                                                                                  ID:   ta.Anchor(td.NotZero(), uint64(0)).(uint64),
                                                                                  Name: "Bob",
                                                                                  Age:  26,
                                                                                })
                                                                              

                                                                              It fails if no request has been sent yet.

                                                                              func (*TestAPI) Delete

                                                                              func (t *TestAPI) Delete(target string, body io.Reader, headers ...interface{}) *TestAPI

                                                                                Delete sends a HTTP DELETE to the tested API. Any Cmp* or NoBody methods can now be called.

                                                                                Note that Failed() status is reset just after this call.

                                                                                See NewRequest for all possible formats accepted in headers.

                                                                                func (*TestAPI) DeleteJSON

                                                                                func (t *TestAPI) DeleteJSON(target string, body interface{}, headers ...interface{}) *TestAPI

                                                                                  DeleteJSON sends a HTTP DELETE with body marshaled to JSON. "Content-Type" header is automatically set to "application/json". Any Cmp* or NoBody methods can now be called.

                                                                                  Note that Failed() status is reset just after this call.

                                                                                  See NewRequest for all possible formats accepted in headers.

                                                                                  func (*TestAPI) DeleteXML

                                                                                  func (t *TestAPI) DeleteXML(target string, body interface{}, headers ...interface{}) *TestAPI

                                                                                    DeleteXML sends a HTTP DELETE with body marshaled to XML. "Content-Type" header is automatically set to "application/xml". Any Cmp* or NoBody methods can now be called.

                                                                                    Note that Failed() status is reset just after this call.

                                                                                    See NewRequest for all possible formats accepted in headers.

                                                                                    func (*TestAPI) Failed

                                                                                    func (t *TestAPI) Failed() bool

                                                                                      Failed returns true if any Cmp* or NoBody method failed since last request sending.

                                                                                      func (*TestAPI) Get

                                                                                      func (t *TestAPI) Get(target string, headers ...interface{}) *TestAPI

                                                                                        Get sends a HTTP GET to the tested API. Any Cmp* or NoBody methods can now be called.

                                                                                        Note that Failed() status is reset just after this call.

                                                                                        See NewRequest for all possible formats accepted in headers.

                                                                                        func (*TestAPI) Head

                                                                                        func (t *TestAPI) Head(target string, headers ...interface{}) *TestAPI

                                                                                          Head sends a HTTP HEAD to the tested API. Any Cmp* or NoBody methods can now be called.

                                                                                          Note that Failed() status is reset just after this call.

                                                                                          See NewRequest for all possible formats accepted in headers.

                                                                                          func (*TestAPI) Name

                                                                                          func (t *TestAPI) Name(args ...interface{}) *TestAPI

                                                                                            Name allows to name the series of tests that follow. This name is used as a prefix for all following tests, in case of failure to qualify each test. If len(args) > 1 and the first item of "args" is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else "args" are passed to fmt.Fprint.

                                                                                            func (*TestAPI) NewJSONRequest

                                                                                            func (t *TestAPI) NewJSONRequest(method, target string, body interface{}, headers ...interface{}) *TestAPI

                                                                                              NewJSONRequest sends a HTTP request with body marshaled to JSON. "Content-Type" header is automatically set to "application/json". Any Cmp* or NoBody methods can now be called.

                                                                                              Note that Failed() status is reset just after this call.

                                                                                              See NewRequest for all possible formats accepted in headers.

                                                                                              func (*TestAPI) NewXMLRequest

                                                                                              func (t *TestAPI) NewXMLRequest(method, target string, body interface{}, headers ...interface{}) *TestAPI

                                                                                                NewXMLRequest sends a HTTP request with body marshaled to XML. "Content-Type" header is automatically set to "application/xml". Any Cmp* or NoBody methods can now be called.

                                                                                                Note that Failed() status is reset just after this call.

                                                                                                See NewRequest for all possible formats accepted in headers.

                                                                                                func (*TestAPI) NoBody

                                                                                                func (t *TestAPI) NoBody() *TestAPI

                                                                                                  NoBody tests that the last request response body is empty.

                                                                                                  It fails if no request has been sent yet.

                                                                                                  func (*TestAPI) Or

                                                                                                  func (t *TestAPI) Or(fn interface{}) *TestAPI

                                                                                                    Or executes function "fn" if t.Failed() is true at the moment it is called.

                                                                                                    "fn" can have several types:

                                                                                                    - func(body string) or func(t *td.T, body string)
                                                                                                      → "fn" is called with response body as a string.
                                                                                                        If no response has been received yet, body is "";
                                                                                                    - func(body []byte) or func(t *td.T, body []byte)
                                                                                                      → "fn" is called with response body as a []byte.
                                                                                                        If no response has been received yet, body is nil;
                                                                                                    - func(t *td.T, resp *httptest.ResponseRecorder)
                                                                                                      → "fn" is called with the internal object containing the response.
                                                                                                        See net/http/httptest for details.
                                                                                                        If no response has been received yet, resp is nil.
                                                                                                    

                                                                                                    If "fn" type is not one of these types, it panics.

                                                                                                    func (*TestAPI) OrDumpResponse

                                                                                                    func (t *TestAPI) OrDumpResponse() *TestAPI

                                                                                                      OrDumpResponse dumps the response if at least one previous test failed.

                                                                                                      ta := tdhttp.NewTestAPI(t, handler)
                                                                                                      
                                                                                                      ta.Get("/foo").
                                                                                                        CmpStatus(200).
                                                                                                        OrDumpResponse(). // if status check failed, dumps the response
                                                                                                        CmpBody("bar")    // if it fails, the response is not dumped
                                                                                                      
                                                                                                      ta.Get("/foo").
                                                                                                        CmpStatus(200).
                                                                                                        CmpBody("bar").
                                                                                                        OrDumpResponse() // dumps the response if status and/or body checks fail
                                                                                                      

                                                                                                      See AutoDumpResponse method to automatize this dump.

                                                                                                      func (*TestAPI) Patch

                                                                                                      func (t *TestAPI) Patch(target string, body io.Reader, headers ...interface{}) *TestAPI

                                                                                                        Patch sends a HTTP PATCH to the tested API. Any Cmp* or NoBody methods can now be called.

                                                                                                        Note that Failed() status is reset just after this call.

                                                                                                        See NewRequest for all possible formats accepted in headers.

                                                                                                        func (*TestAPI) PatchJSON

                                                                                                        func (t *TestAPI) PatchJSON(target string, body interface{}, headers ...interface{}) *TestAPI

                                                                                                          PatchJSON sends a HTTP PATCH with body marshaled to JSON. "Content-Type" header is automatically set to "application/json". Any Cmp* or NoBody methods can now be called.

                                                                                                          Note that Failed() status is reset just after this call.

                                                                                                          See NewRequest for all possible formats accepted in headers.

                                                                                                          func (*TestAPI) PatchXML

                                                                                                          func (t *TestAPI) PatchXML(target string, body interface{}, headers ...interface{}) *TestAPI

                                                                                                            PatchXML sends a HTTP PATCH with body marshaled to XML. "Content-Type" header is automatically set to "application/xml". Any Cmp* or NoBody methods can now be called.

                                                                                                            Note that Failed() status is reset just after this call.

                                                                                                            See NewRequest for all possible formats accepted in headers.

                                                                                                            func (*TestAPI) Post

                                                                                                            func (t *TestAPI) Post(target string, body io.Reader, headers ...interface{}) *TestAPI

                                                                                                              Post sends a HTTP POST to the tested API. Any Cmp* or NoBody methods can now be called.

                                                                                                              Note that Failed() status is reset just after this call.

                                                                                                              See NewRequest for all possible formats accepted in headers.

                                                                                                              func (*TestAPI) PostForm

                                                                                                              func (t *TestAPI) PostForm(target string, data url.Values, headers ...interface{}) *TestAPI

                                                                                                                PostForm sends a HTTP POST with data's keys and values URL-encoded as the request body to the tested API.. "Content-Type" header is automatically set to "application/x-www-form-urlencoded". Any Cmp* or NoBody methods can now be called.

                                                                                                                Note that Failed() status is reset just after this call.

                                                                                                                See NewRequest for all possible formats accepted in headers.

                                                                                                                func (*TestAPI) PostJSON

                                                                                                                func (t *TestAPI) PostJSON(target string, body interface{}, headers ...interface{}) *TestAPI

                                                                                                                  PostJSON sends a HTTP POST with body marshaled to JSON. "Content-Type" header is automatically set to "application/json". Any Cmp* or NoBody methods can now be called.

                                                                                                                  Note that Failed() status is reset just after this call.

                                                                                                                  See NewRequest for all possible formats accepted in headers.

                                                                                                                  func (*TestAPI) PostXML

                                                                                                                  func (t *TestAPI) PostXML(target string, body interface{}, headers ...interface{}) *TestAPI

                                                                                                                    PostXML sends a HTTP POST with body marshaled to XML. "Content-Type" header is automatically set to "application/xml". Any Cmp* or NoBody methods can now be called.

                                                                                                                    Note that Failed() status is reset just after this call.

                                                                                                                    See NewRequest for all possible formats accepted in headers.

                                                                                                                    func (*TestAPI) Put

                                                                                                                    func (t *TestAPI) Put(target string, body io.Reader, headers ...interface{}) *TestAPI

                                                                                                                      Put sends a HTTP PUT to the tested API. Any Cmp* or NoBody methods can now be called.

                                                                                                                      Note that Failed() status is reset just after this call.

                                                                                                                      See NewRequest for all possible formats accepted in headers.

                                                                                                                      func (*TestAPI) PutJSON

                                                                                                                      func (t *TestAPI) PutJSON(target string, body interface{}, headers ...interface{}) *TestAPI

                                                                                                                        PutJSON sends a HTTP PUT with body marshaled to JSON. "Content-Type" header is automatically set to "application/json". Any Cmp* or NoBody methods can now be called.

                                                                                                                        Note that Failed() status is reset just after this call.

                                                                                                                        See NewRequest for all possible formats accepted in headers.

                                                                                                                        func (*TestAPI) PutXML

                                                                                                                        func (t *TestAPI) PutXML(target string, body interface{}, headers ...interface{}) *TestAPI

                                                                                                                          PutXML sends a HTTP PUT with body marshaled to XML. "Content-Type" header is automatically set to "application/xml". Any Cmp* or NoBody methods can now be called.

                                                                                                                          Note that Failed() status is reset just after this call.

                                                                                                                          See NewRequest for all possible formats accepted in headers.

                                                                                                                          func (*TestAPI) Request

                                                                                                                          func (t *TestAPI) Request(req *http.Request) *TestAPI

                                                                                                                            Request sends a new HTTP request to the tested API. Any Cmp* or NoBody methods can now be called.

                                                                                                                            Note that Failed() status is reset just after this call.

                                                                                                                            func (*TestAPI) Run

                                                                                                                            func (t *TestAPI) Run(name string, f func(t *TestAPI)) bool

                                                                                                                              Run runs "f" as a subtest of t called "name".

                                                                                                                              func (*TestAPI) SentAt

                                                                                                                              func (t *TestAPI) SentAt() time.Time

                                                                                                                                SentAt returns the time just before the last request is handled. It can be used to check the time a route sets and returns, as in:

                                                                                                                                ta.PostJSON("/person/42", Person{Name: "Bob", Age: 23}).
                                                                                                                                  CmpStatus(http.StatusCreated).
                                                                                                                                  CmpJSONBody(Person{
                                                                                                                                    ID:        ta.A(td.NotZero(), uint64(0)).(uint64),
                                                                                                                                    Name:      "Bob",
                                                                                                                                    Age:       23,
                                                                                                                                    CreatedAt: ta.A(td.Between(ta.SentAt(), time.Now())).(time.Time),
                                                                                                                                  })
                                                                                                                                

                                                                                                                                checks that CreatedAt is included between the time when the request has been sent, and the time when the comparison occurs.

                                                                                                                                func (*TestAPI) T

                                                                                                                                func (t *TestAPI) T() *td.T

                                                                                                                                  T returns the internal instance of *td.T.

                                                                                                                                  func (*TestAPI) With

                                                                                                                                  func (t *TestAPI) With(tb testing.TB) *TestAPI

                                                                                                                                    With creates a new *TestAPI instance copied from "t", but resetting the testing.TB instance the tests are based on to "tb". The returned instance is independent from "t", sharing only the same handler.

                                                                                                                                    It is typically used when the *TestAPI instance is "reused" in sub-tests, as in:

                                                                                                                                    func TestMyAPI(t *testing.T) {
                                                                                                                                      ta := tdhttp.NewTestAPI(t, MyAPIHandler())
                                                                                                                                    
                                                                                                                                      ta.Get("/test").CmpStatus(200)
                                                                                                                                    
                                                                                                                                      t.Run("errors", func (t *testing.T) {
                                                                                                                                        ta := ta.With(t)
                                                                                                                                    
                                                                                                                                        ta.Get("/test?bad=1").CmpStatus(400)
                                                                                                                                        ta.Get("/test?bad=buzz").CmpStatus(400)
                                                                                                                                      }
                                                                                                                                    
                                                                                                                                      ta.Get("/next").CmpStatus(200)
                                                                                                                                    }
                                                                                                                                    

                                                                                                                                    Note that "tb" can be a *testing.T as well as a *td.T.

                                                                                                                                    See Run method for another way to handle subtests.

                                                                                                                                    Directories

                                                                                                                                    Path Synopsis