Improve test suite (#628)
Signed-off-by: Thomas Miceli <tho.miceli@gmail.com>
هذا الالتزام موجود في:
277
test.md
Normal file
277
test.md
Normal file
@@ -0,0 +1,277 @@
|
||||
---
|
||||
description: Testing handler and middleware
|
||||
slug: /testing
|
||||
sidebar_position: 13
|
||||
---
|
||||
|
||||
# Testing
|
||||
|
||||
## Testing Handler
|
||||
|
||||
`GET` `/users/:id`
|
||||
|
||||
Handler below retrieves user by id from the database. If user is not found it returns
|
||||
`404` error with a message.
|
||||
|
||||
### CreateUser
|
||||
|
||||
`POST` `/users`
|
||||
|
||||
- Accepts JSON payload
|
||||
- On success `201 - Created`
|
||||
- On error `500 - Internal Server Error`
|
||||
|
||||
### GetUser
|
||||
|
||||
`GET` `/users/:email`
|
||||
|
||||
- On success `200 - OK`
|
||||
- On error `404 - Not Found` if user is not found otherwise `500 - Internal Server Error`
|
||||
|
||||
`handler.go`
|
||||
|
||||
```go
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
)
|
||||
|
||||
type (
|
||||
User struct {
|
||||
Name string `json:"name" form:"name"`
|
||||
Email string `json:"email" form:"email"`
|
||||
}
|
||||
handler struct {
|
||||
db map[string]*User
|
||||
}
|
||||
)
|
||||
|
||||
func (h *handler) createUser(c *echo.Context) error {
|
||||
u := new(User)
|
||||
if err := c.Bind(u); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(http.StatusCreated, u)
|
||||
}
|
||||
|
||||
func (h *handler) getUser(c *echo.Context) error {
|
||||
email := c.Param("email")
|
||||
user := h.db[email]
|
||||
if user == nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, "user not found")
|
||||
}
|
||||
return c.JSON(http.StatusOK, user)
|
||||
}
|
||||
```
|
||||
|
||||
`handler_test.go`
|
||||
|
||||
```go
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/labstack/echo/v5/echotest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
mockDB = map[string]*User{
|
||||
"jon@labstack.com": &User{"Jon Snow", "jon@labstack.com"},
|
||||
}
|
||||
userJSON = `{"name":"Jon Snow","email":"jon@labstack.com"}`
|
||||
)
|
||||
|
||||
func TestCreateUser(t *testing.T) {
|
||||
// Setup
|
||||
e := echo.New()
|
||||
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(userJSON))
|
||||
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
c := e.NewContext(req, rec)
|
||||
|
||||
h := &controller{mockDB}
|
||||
|
||||
// Assertions
|
||||
if assert.NoError(t, h.createUser(c)) {
|
||||
assert.Equal(t, http.StatusCreated, rec.Code)
|
||||
assert.Equal(t, userJSON, rec.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
// Same test as above but using `echotest` package helpers
|
||||
func TestCreateUserWithEchoTest(t *testing.T) {
|
||||
c, rec := echotest.ContextConfig{
|
||||
Headers: map[string][]string{
|
||||
echo.HeaderContentType: {echo.MIMEApplicationJSON},
|
||||
},
|
||||
JSONBody: []byte(`{"name":"Jon Snow","email":"jon@labstack.com"}`),
|
||||
}.ToContextRecorder(t)
|
||||
|
||||
h := &controller{mockDB}
|
||||
|
||||
// Assertions
|
||||
if assert.NoError(t, h.createUser(c)) {
|
||||
assert.Equal(t, http.StatusCreated, rec.Code)
|
||||
assert.Equal(t, userJSON+"\n", rec.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
// Same test as above but even shorter
|
||||
func TestCreateUserWithEchoTest2(t *testing.T) {
|
||||
h := &controller{mockDB}
|
||||
|
||||
rec := echotest.ContextConfig{
|
||||
Headers: map[string][]string{
|
||||
echo.HeaderContentType: {echo.MIMEApplicationJSON},
|
||||
},
|
||||
JSONBody: []byte(`{"name":"Jon Snow","email":"jon@labstack.com"}`),
|
||||
}.ServeWithHandler(t, h.createUser)
|
||||
|
||||
assert.Equal(t, http.StatusCreated, rec.Code)
|
||||
assert.Equal(t, userJSON+"\n", rec.Body.String())
|
||||
}
|
||||
|
||||
func TestGetUser(t *testing.T) {
|
||||
// Setup
|
||||
e := echo.New()
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
c := e.NewContext(req, rec)
|
||||
|
||||
c.SetPath("/users/:email")
|
||||
c.SetPathValues(echo.PathValues{
|
||||
{Name: "email", Value: "jon@labstack.com"},
|
||||
})
|
||||
h := &controller{mockDB}
|
||||
|
||||
// Assertions
|
||||
if assert.NoError(t, h.getUser(c)) {
|
||||
assert.Equal(t, http.StatusOK, rec.Code)
|
||||
assert.Equal(t, userJSON, rec.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUserWithEchoTest(t *testing.T) {
|
||||
c, rec := echotest.ContextConfig{
|
||||
PathValues: echo.PathValues{
|
||||
{Name: "email", Value: "jon@labstack.com"},
|
||||
},
|
||||
Headers: map[string][]string{
|
||||
echo.HeaderContentType: {echo.MIMEApplicationJSON},
|
||||
},
|
||||
JSONBody: []byte(userJSON),
|
||||
}.ToContextRecorder(t)
|
||||
|
||||
h := &controller{mockDB}
|
||||
|
||||
// Assertions
|
||||
if assert.NoError(t, h.getUser(c)) {
|
||||
assert.Equal(t, http.StatusOK, rec.Code)
|
||||
assert.Equal(t, userJSON+"\n", rec.Body.String())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Using Form Payload
|
||||
|
||||
```go
|
||||
// import "net/url"
|
||||
f := make(url.Values)
|
||||
f.Set("name", "Jon Snow")
|
||||
f.Set("email", "jon@labstack.com")
|
||||
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode()))
|
||||
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationForm)
|
||||
```
|
||||
|
||||
Multipart form payload:
|
||||
```go
|
||||
func TestContext_MultipartForm(t *testing.T) {
|
||||
testConf := echotest.ContextConfig{
|
||||
MultipartForm: &echotest.MultipartForm{
|
||||
Fields: map[string]string{
|
||||
"key": "value",
|
||||
},
|
||||
Files: []echotest.MultipartFormFile{
|
||||
{
|
||||
Fieldname: "file",
|
||||
Filename: "test.json",
|
||||
Content: echotest.LoadBytes(t, "testdata/test.json"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
c := testConf.ToContext(t)
|
||||
|
||||
assert.Equal(t, "value", c.FormValue("key"))
|
||||
assert.Equal(t, http.MethodPost, c.Request().Method)
|
||||
assert.Equal(t, true, strings.HasPrefix(c.Request().Header.Get(echo.HeaderContentType), "multipart/form-data; boundary="))
|
||||
|
||||
fv, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, "test.json", fv.Filename)
|
||||
}
|
||||
```
|
||||
|
||||
### Setting Path Params
|
||||
|
||||
```go
|
||||
c.SetPathValues(echo.PathValues{
|
||||
{Name: "id", Value: "1"},
|
||||
{Name: "email", Value: "jon@labstack.com"},
|
||||
})
|
||||
```
|
||||
|
||||
### Setting Query Params
|
||||
|
||||
```go
|
||||
// import "net/url"
|
||||
q := make(url.Values)
|
||||
q.Set("email", "jon@labstack.com")
|
||||
req := httptest.NewRequest(http.MethodGet, "/?"+q.Encode(), nil)
|
||||
```
|
||||
|
||||
## Testing Middleware
|
||||
|
||||
```go
|
||||
func TestCreateUserWithEchoTest2(t *testing.T) {
|
||||
handler := func(c *echo.Context) error {
|
||||
return c.JSON(http.StatusTeapot, fmt.Sprintf("email: %s", c.Param("email")))
|
||||
}
|
||||
middleware := func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c *echo.Context) error {
|
||||
c.Set("user_id", int64(1234))
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
|
||||
c, rec := echotest.ContextConfig{
|
||||
PathValues: echo.PathValues{{Name: "email", Value: "jon@labstack.com"}},
|
||||
}.ToContextRecorder(t)
|
||||
|
||||
err := middleware(handler)(c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// check that middleware set the value
|
||||
userID, err := echo.ContextGet[int64](c, "user_id")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1234), userID)
|
||||
|
||||
// check that handler returned the correct response
|
||||
assert.Equal(t, http.StatusTeapot, rec.Code)
|
||||
}
|
||||
```
|
||||
|
||||
For now you can look into built-in middleware [test cases](https://github.com/labstack/echo/tree/master/middleware).
|
||||
المرجع في مشكلة جديدة
حظر مستخدم