HTTP Tests
介绍
在构建Web应用程序时,您通常需要从头到尾测试您的HTTP请求是否正常工作。Goravel的测试工具使这变得简单——您可以模拟请求并验证响应,而无需设置复杂的测试环境。
发起 HTTP 请求
测试 Goravel 中的 HTTP 接口使用了一个简单的模式。使用 TestCase
的 Http
方法,它需要一个 *testing.T
参数用于断言。该方法将返回一个实例(framework/contracts/testing.TestRequest
),它处理所有常见的 HTTP 请求,如 Get
、Post
和 Put
。
不需要真正的 HTTP 调用,这些方法在内部模拟了应用程序的请求周期。每个请求都返回一个响应对象(framework/contracts/testing.TestResponse
),其中包含检查结果的方法。
一个简单的例子:
func (s *ExampleTestSuite) TestIndex() {
response, err := s.Http(s.T()).Get("/users/1")
s.Nil(err)
response.AssertStatus(200)
}
自定义请求头
你可以使用 WithHeader
设置单个请求头,或者使用 WithHeaders
设置多个请求头:
func (s *ExampleTestSuite) TestIndex() {
// Single header
response, err := s.Http(s.T()).WithHeader("X-Custom-Header", "Value").Get("/users/1")
// Multiple headers
response, err := s.Http(s.T()).WithHeaders(map[string]string{
"X-Custom-Header": "Value",
"Accept": "application/json",
}).Get("/users/1")
}
Cookies
你可以使用 WithCookie
或 WithCookies
方法在发起请求前设置 cookie 值。
func (s *ExampleTestSuite) TestIndex() {
response, err := s.Http(s.T()).WithCookie("name", "krishan").Get("/users/1")
// or use WithHeaders for multiple Headers
response, err := s.Http(s.T()).WithHeader(map[string]string{
"name": "krishan",
"lang": "en",
}).Get("/users/1")
}
WithSession
你可以使用 WithSession
方法设置 session 数据:
func (s *ExampleTestSuite) TestIndex() {
response, err := s.Http(s.T()).WithSession(map[string]any{"role": "admin"}).Get("/users/1")
}
调试响应
在发起请求后,你可以使用 Session
、Headers
、Content
、Cookies
或 Json
方法来检查从请求返回的数据。
func (s *ExampleTestSuite) TestIndex() {
response, err := s.Http(s.T()).WithSession(map[string]any{"role": "admin"}).Get("/users/1")
content, err := response.Content()
cookies := response.Cookies()
headers := response.Headers()
json, err := response.Json() // response body parsed as json(map[string]any)
session, err := response.Session() // returns all values stored in the current request session
}
构建请求体
如 Post
、Put
、Delete
等方法。Goravel 接受 io.Reader
作为第二个参数。为了简化构建请求体,框架提供了用于构建请求体的实用方法。
import "github.com/goravel/framework/support/http"
func (s *ExampleTestSuite) TestIndex() {
builder := http.NewBody().SetField("name", "krishan")
body, err := builder.Build()
response, err := s.Http(s.T()).WithHeader("Content-Type", body.ContentType()).Post("/users", body)
}
测试 JSON API
Goravel 提供了多个帮助方法来有效地测试 JSON API 响应。它尝试将响应体解析为 Go map[string]any
。如果解析失败,相关的断言也会失败。
func (s *ExampleTestSuite) TestIndex() {
response, err := s.Http(s.T()).WithHeader("Content-Type", body.ContentType()).Post("/users", nil)
s.Nil(err)
response.AssertStatus(201).
AssertJson(map[string]any{
"created": true,
})
}
使用 TestResponse
的 Json
方法可以直接访问解析后的 JSON,这样你可以检查响应体的各个元素。
json, err := response.Json()
s.Nil(err)
s.True(json["created"])
提示
AssertJson
方法检查响应是否包含所有指定的值,即使响应包含额外的字段。除非使用 AssertExactJson
,否则不需要完全匹配。
断言精确匹配的 JSON
如果需要验证响应是否完全匹配您的预期 JSON(没有额外或缺少的字段),请使用 AssertExactJson
方法。
func (s *ExampleTestSuite) TestIndex() {
response, err := s.Http(s.T()).WithHeader("Content-Type", body.ContentType()).Post("/users", nil)
s.Nil(err)
response.AssertStatus(201).
AssertExactJson(map[string]any{
"created": true,
})
}
Fluent JSON Testing
Goravel 可以轻松地对 JSON 响应执行流畅的断言。使用 AssertFluentJson
方法,你可以传递一个闭包,该闭包提供了 framework/contracts/testing.AssertableJSON
的实例。这个实例允许你检查请求返回的 JSON 响应中的特定值或条件。
例如,你可以使用 Where
方法来断言 JSON 响应中是否存在特定值,使用 Missing
方法来确保属性不存在。
import contractstesting "github.com/goravel/framework/contracts/testing"
func (s *ExampleTestSuite) TestIndex() {
response, err := s.Http(s.T()).Get("/users/1")
s.Nil(err)
response.AssertStatus(201).
AssertFluentJson(func (json contractstesting.AssertableJSON) {
json.Where("id", float64(1)).
Where("name", "bowen").
WhereNot("lang", "en").
Missing("password")
})
}
断言属性的存在/缺失
如果你想检查属性是否存在或缺失,可以使用 Has
和 Missing
方法:
response.AssertStatus(201).
AssertFluentJson(func (json contractstesting.AssertableJSON) {
json.Has("username").
Missing("password")
})
你也可以一次性断言多个属性的存在或缺失,使用 HasAll
和 MissingAll
:
response.AssertStatus(201).
AssertFluentJson(func (json contractstesting.AssertableJSON) {
json.Has([]string{"username", "email"}).
MissingAll([]string{"verified", "password"})
})
如果你只需要检查列表中至少一个属性的存在,请使用 HasAny
方法:
response.AssertStatus(201).
AssertFluentJson(func (json contractstesting.AssertableJSON) {
json.HasAny([]string{"username", "email"})
})
断言 JSON 集合
当响应包含一个对象集合时,可以使用各种方法来断言其结构和内容。
type Item struct {
ID int `json:"id"`
}
facades.Route().Get("/", func(ctx http.Context) http.Response {
items := []Item{
{ID: 1},
{ID: 2},
}
return ctx.Response().Json(200, map[string]{
"items": items,
})
}
你可以使用 Count
方法验证集合中元素的数量。要断言第一个元素的属性,请使用 First
方法,该方法提供了一个 AssertableJson
实例。同样,使用 Each
方法可以遍历所有元素并逐个断言其属性。另外,HasWithScope
方法结合了 First
和 Count
的功能,允许你断言第一个元素及其内容,同时为范围断言提供一个 AssertableJson
实例。
// Count and First
response.AssertStatus(200).
AssertFluentJson(func(json contractstesting.AssertableJSON) {
json.Count("items", 2).
First("items", func(json contractstesting.AssertableJSON) {
json.Where("id", 1)
})
})
// Each
response.AssertStatus(200).
AssertFluentJson(func(json contractstesting.AssertableJSON) {
json.Count("items", 2).
Each("items", func(json contractstesting.AssertableJSON) {
json.Has("id")
})
})
// HasWithScope
response.AssertStatus(200).
AssertFluentJson(func(json contractstesting.AssertableJSON) {
json.HasWithScope("items", 2, func(json contractstesting.AssertableJSON) {
json.Where("id", 1)
})
})
可用断言
断言响应
AssertAccepted
断言响应的 HTTP 状态码为 202 Accepted
:
response.AssertAccepted()
AssertBadRequest
断言响应的 HTTP 状态码为 400 Bad Request
:
response.AssertBadRequest()
AssertConflict
断言响应的 HTTP 状态码为 409 Conflict
:
response.AssertConflict()
AssertCookie
断言响应包含指定名称和值的 cookie:
response.AssertCookie("name", "value")
AssertCookieExpired
断言指定的 cookie 已过期:
response.AssertCookieExpired("name")
AssertCookieMissing
断言响应不包含指定名称的 cookie:
response.AssertCookieMissing("name")
AssertCookieNotExpired
断言指定的 cookie 未过期:
response.AssertCookieNotExpired("name")
AssertCreated
断言响应的 HTTP 状态码为 201 Created
:
response.AssertCreated()
AssertDontSee
断言响应不包含指定的值。第二个参数(可选)确定在检查之前是否转义值中的特殊字符。如果未提供,则默认为 true
。
response.AssertDontSee([]string{"<div>"}, false) // Do not escape special characters
AssertExactJson
断言响应的 JSON 与提供的 map[string]any
完全匹配:
response.AssertExactJson(map[string]any{"created": true})
AssertFluentJson
使用 JSON 流畅接口进行断言:
import contractstesting "github.com/goravel/framework/contracts/testing"
response.AssertFluentJson(func(json contractstesting.AssertableJSON) {
json.Where("created", true)
})
AssertForbidden
断言响应的 HTTP 状态码为 403 Forbidden
:
response.AssertForbidden()
AssertFound
断言响应的 HTTP 状态码为 302 Found
:
response.AssertFound()
AssertGone
断言响应的 HTTP 状态码为 410 Gone
:
response.AssertGone()
AssertHeader
断言响应包含指定名称和值的 header:
response.AssertHeader("Content-Type", "application/json")
AssertHeaderMissing
断言响应不包含指定名称的 header:
response.AssertHeaderMissing("X-Custom-Header")
AssertInternalServerError
断言响应的 HTTP 状态码为 500 Internal Server Error
:
response.AssertInternalServerError()
AssertJson
断言响应的 JSON 包含提供的片段:
response.AssertJson(map[string]any{"created": true})
AssertJsonMissing
断言响应的 JSON 不包含指定的键或值:
response.AssertJsonMissing(map[string]any{"created": false})
AssertMethodNotAllowed
断言响应的 HTTP 状态码为 405 Method Not Allowed
:
response.AssertMethodNotAllowed()
AssertMovedPermanently
断言响应的 HTTP 状态码为 301 Moved Permanently
:
response.AssertMovedPermanently()
AssertNoContent
断言响应的 HTTP 状态码为 204 No Content
:
response.AssertNoContent()
AssertNotAcceptable
断言响应的 HTTP 状态码为 406 Not Acceptable
:
response.AssertNotAcceptable()
AssertNotFound
断言响应的 HTTP 状态码为 404 Not Found
:
response.AssertNotFound()
AssertNotModified
断言响应的 HTTP 状态码为 304 Not Modified
:
response.AssertNotModified()
AssertOk
断言响应的 HTTP 状态码为 200 OK
:
response.AssertOk()
AssertPartialContent
断言响应的 HTTP 状态码为 206 Partial Content
:
response.AssertPartialContent()
AssertPaymentRequired
断言响应的 HTTP 状态码为 402 Payment Required
:
response.AssertPaymentRequired()
AssertRequestTimeout
断言响应的 HTTP 状态码为 408 Request Timeout
:
response.AssertRequestTimeout()
AssertSee
断言响应包含指定的值。第二个参数(可选)确定在检查之前是否转义值中的特殊字符。如果未提供,则默认为 true
。
response.AssertSee([]string{"<div>"}, false) // Do not escape special characters
AssertSeeInOrder
断言响应按给定顺序包含指定的值。第二个参数(可选)确定在检查之前是否转义值中的特殊字符。如果未提供,则默认为 true
。
response.AssertSeeInOrder([]string{"First", "Second"}, false) // Do not escape special characters
AssertServerError
断言响应的 HTTP 状态码为 >= 500 , < 600
:
response.AssertServerError()
AssertServiceUnavailable
断言响应的 HTTP 状态码为 503 Service Unavailable
:
response.AssertServiceUnavailable()
AssertStatus
断言响应的 HTTP 状态码为指定的值:
response.AssertStatus(200)
AssertSuccessful
断言响应的 HTTP 状态码为成功的状态码(2xx):
response.AssertSuccessful()
AssertTemporaryRedirect
断言响应的 HTTP 状态码为 307 Temporary Redirect
:
response.AssertTemporaryRedirect()
AssertTooManyRequests
断言响应的 HTTP 状态码为 429 Too Many Requests
:
response.AssertTooManyRequests()
AssertUnauthorized
断言响应的 HTTP 状态码为 401 Unauthorized
:
response.AssertUnauthorized()
AssertUnprocessableEntity
断言响应的 HTTP 状态码为 422 Unprocessable Entity
:
response.AssertUnprocessableEntity()