import (
"testing"
td "github.com/maxatome/go-testdeep"
)
func TestAssertionsAndRequirements(t *testing.T) {
assert := td.NewT(t)
require := assert.FailureIsFatal()
got := SomeFunction()
require.Cmp(got, expected) // if it fails: report error + abort
assert.Cmp(got, expected) // if it fails: report error + continue
}
io.Reader
contents, like http.Response.Body for example?The Smuggle
operator is done for that,
here with the help of ReadAll
.
import (
"net/http"
"testing"
"io/ioutil"
td "github.com/maxatome/go-testdeep"
)
func TestResponseBody(t *testing.T) {
// Expect this response sends "Expected Response!"
var resp *http.Response = GetResponse()
td.Cmp(t, resp.Body,
td.Smuggle(ioutil.ReadAll, []byte("Expected Response!")))
}
string
s instead of byte
sNo problem, ReadAll
the
body by yourself and cast returned []byte
contents to string
,
still using Smuggle
operator:
import (
"io"
"io/ioutil"
"net/http"
"testing"
td "github.com/maxatome/go-testdeep"
)
func TestResponseBody(t *testing.T) {
// Expect this response sends "Expected Response!"
var resp *http.Response = GetResponse()
td.Cmp(t, body, td.Smuggle(
func(body io.Reader) (string, error) {
b, err := ioutil.ReadAll(body)
return string(b), err
},
"Expected Response!"))
}
No problem, JSON unmarshal it just after reading the body:
import (
"encoding/json"
"io"
"io/ioutil"
"net/http"
"testing"
td "github.com/maxatome/go-testdeep"
)
func TestResponseBody(t *testing.T) {
// Expect this response sends `{"ID":42,"Name":"Bob","Age":28}`
var resp *http.Response = GetResponse()
type Person struct {
ID uint64
Name string
Age int
}
td.Cmp(t, body, td.Smuggle(
func(body io.Reader) (&Person, error) {
b, err := ioutil.ReadAll(body)
if err != nil {
return nil, err
}
var s Person
return &s, json.Unmarshal(b, &s)
},
&Person{
ID: 42,
Name: "Bob",
Age: 28,
}))
}
No problem, use Struct
operator to test
that ID field is non-zero:
import (
"encoding/json"
"io"
"io/ioutil"
"net/http"
"testing"
td "github.com/maxatome/go-testdeep"
)
func TestResponseBody(t *testing.T) {
// Expect this response sends `{"ID":42,"Name":"Bob","Age":28}`
var resp *http.Response = GetResponse()
type Person struct {
ID uint64
Name string
Age int
}
td.Cmp(t, body, td.Smuggle(
func(body io.Reader) (*Person, error) {
b, err := ioutil.ReadAll(body)
if err != nil {
return nil, err
}
var s Person
return &s, json.Unmarshal(b, &s)
},
td.Struct(&Person{
Name: "Bob",
Age: 28,
}, td.StructFields{
"ID": td.NotZero(),
})))
}
tdhttp
helper
is done for that!
import (
"encoding/json"
"net/http"
"testing"
td "github.com/maxatome/go-testdeep"
"github.com/maxatome/go-testdeep/helpers/tdhttp"
)
type Person struct {
ID uint64
Name string
Age int
}
// MyApi defines our API.
func MyAPI() *http.ServeMux {
mux := http.NewServeMux()
// GET /json
mux.HandleFunc("/json", func(w http.ResponseWriter, req *http.Request) {
if req.Method != "GET" {
http.NotFound(w, req)
return
}
b, err := json.Marshal(Person{
ID: 42,
Name: "Bob",
Age: 28,
})
if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(b)
})
return mux
}
func TestMyApi(t *testing.T) {
myAPI := MyAPI()
tdhttp.CmpJSONResponse(t,
tdhttp.Get("/json"),
myAPI.ServeHTTP,
tdhttp.Response{
Status: http.StatusOK,
// Header can be tested too… See tdhttp doc.
Body: td.Struct(&Person{
Name: "Bob",
Age: 28,
}, td.StructFields{
"ID": td.NotZero(),
}),
},
"Testing GET /json")
}
net/http
handlersIt is exactly the same as for net/http
handlers as *gin.Engine
implements http.Handler
interface!
So keep using
tdhttp
helper:
import (
"net/http"
"testing"
"github.com/gin-gonic/gin"
td "github.com/maxatome/go-testdeep"
"github.com/maxatome/go-testdeep/helpers/tdhttp"
)
type Person struct {
ID uint64
Name string
Age int
}
// MyGinGonicApi defines our API.
func MyGinGonicAPI() *gin.Engine {
router := gin.Default() // or gin.New() or receive the router by param it doesn't matter
router.GET("/json", func(c *gin.Context) {
c.JSON(http.StatusOK, Person{
ID: 42,
Name: "Bob",
Age: 28,
})
})
return router
}
func TestMyGinGonicApi(t *testing.T) {
myAPI := MyGinGonicAPI()
tdhttp.CmpJSONResponse(t,
tdhttp.Get("/json"),
myAPI.ServeHTTP,
tdhttp.Response{
Status: http.StatusOK,
// Header can be tested too… See tdhttp doc.
Body: td.Struct(&Person{
Name: "Bob",
Age: 28,
}, td.StructFields{
"ID": td.NotZero(),
}),
},
"Testing GET /json")
}
In fact you can Catch
the ID before comparing
it to 0. Try:
func TestMyGinGonicApi(t *testing.T) {
myAPI := MyGinGonicAPI()
var id uint64
if tdhttp.CmpJSONResponse(t,
tdhttp.Get("/json"),
myAPI.ServeHTTP,
tdhttp.Response{
Status: http.StatusOK,
// Header can be tested too… See tdhttp doc.
Body: td.Struct(&Person{
Name: "Bob",
Age: 28,
}, td.StructFields{
"ID": td.Catch(&id, td.NotZero()), // ← id set here
}),
},
"Testing GET /json") {
t.Logf("The ID is %d", id)
}
}
With the help of JSON
operator of course! See
it below, used with Catch
(note it can be used
without):
type Person struct {
ID uint64 `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
}
func TestMyGinGonicApi(t *testing.T) {
myAPI := MyGinGonicAPI()
var id uint64
if tdhttp.CmpJSONResponse(t,
tdhttp.Get("/json"),
myAPI.ServeHTTP,
tdhttp.Response{
Status: http.StatusOK,
// Header can be tested too… See tdhttp doc.
Body: td.JSON(`{"id": $id, "name": "Bob", "age": 28}`,
td.Tag("id", td.Catch(&id, td.NotZero())), // $id placeholder
),
},
"Testing GET /json") {
t.Logf("The ID is %d", id)
}
}
Using the environment variable TESTDEEP_MAX_ERRORS
.
TESTDEEP_MAX_ERRORS
contains the maximum number of errors to report
before stopping during one comparison (one
Cmp
execution for example). It defaults to 10
.
Example:
TESTDEEP_MAX_ERRORS=30 go test
Setting it to -1
means no limit:
TESTDEEP_MAX_ERRORS=-1 go test
Using some environment variables:
TESTDEEP_COLOR
enable (on
) or disable (off
) the color
output. It defaults to on
;TESTDEEP_COLOR_TEST_NAME
color of the test name. See below
for color format, it defaults to yellow
;TESTDEEP_COLOR_TITLE
color of the test failure title. See below
for color format, it defaults to cyan
;TESTDEEP_COLOR_OK
color of the test expected value. See below
for color format, it defaults to green
;TESTDEEP_COLOR_BAD
color of the test got value. See below
for color format, it defaults to red
;A color in TESTDEEP_COLOR_*
environment variables has the following
format:
foreground_color # set foreground color, background one untouched
foreground_color:background_color # set foreground AND background color
:background_color # set background color, foreground one untouched
foreground_color
and background_color
can be:
black
red
green
yellow
blue
magenta
cyan
white
gray
For example:
TESTDEEP_COLOR_OK=black:green \
TESTDEEP_COLOR_BAD=white:red \
TESTDEEP_COLOR_TITLE=yellow \
go test
You want to add a new FooBar
operator.
Each time you change example_test.go
, re-run ./tools/gen_funcs.pl .
to update corresponding CmpFooBar
& T.FooBar
examples.
Test coverage must be 100%.