Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 412f08a048 | |||
| ec9d25fef6 | |||
| a600fa8a15 |
+2
-2
@@ -781,7 +781,7 @@ func SetMysqlDB(appIns *Application, config Map) {
|
||||
appIns.Db.Log = appIns.Log
|
||||
appIns.SetConnectDB(func() (master, slave *sql.DB) {
|
||||
query := config.GetString("user") + ":" + config.GetString("password") +
|
||||
"@tcp(" + config.GetString("host") + ":" + config.GetString("port") + ")/" + config.GetString("name") + "?charset=utf8"
|
||||
"@tcp(" + config.GetString("host") + ":" + config.GetString("port") + ")/" + config.GetString("name") + "?charset=utf8mb4"
|
||||
DB, e := sql.Open("mysql", query)
|
||||
if e != nil {
|
||||
appIns.Log.Error().Err(e).Msg("MySQL 主库连接失败")
|
||||
@@ -790,7 +790,7 @@ func SetMysqlDB(appIns *Application, config Map) {
|
||||
configSlave := config.GetMap("slave")
|
||||
if configSlave != nil {
|
||||
query := configSlave.GetString("user") + ":" + configSlave.GetString("password") +
|
||||
"@tcp(" + config.GetString("host") + ":" + configSlave.GetString("port") + ")/" + configSlave.GetString("name") + "?charset=utf8"
|
||||
"@tcp(" + config.GetString("host") + ":" + configSlave.GetString("port") + ")/" + configSlave.GetString("name") + "?charset=utf8mb4"
|
||||
DB1, e := sql.Open("mysql", query)
|
||||
if e != nil {
|
||||
appIns.Log.Error().Err(e).Msg("MySQL 从库连接失败")
|
||||
|
||||
@@ -33,6 +33,8 @@ HoTimeDB是一个基于Golang实现的轻量级ORM框架,参考PHP Medoo设计
|
||||
|
||||
### 初始化数据库连接
|
||||
|
||||
`Application.SetMysqlDB` 默认 DSN 带 `charset=utf8mb4`(兼容 MySQL 8.0 / 8.4,与表字符集对齐)。
|
||||
|
||||
```go
|
||||
import (
|
||||
"code.hoteas.com/golang/hotime/db"
|
||||
@@ -43,7 +45,7 @@ import (
|
||||
|
||||
// 创建连接函数
|
||||
func createConnection() (master, slave *sql.DB) {
|
||||
master, _ = sql.Open("mysql", "user:password@tcp(localhost:3306)/database")
|
||||
master, _ = sql.Open("mysql", "user:password@tcp(localhost:3306)/database?charset=utf8mb4")
|
||||
// slave是可选的,用于读写分离
|
||||
slave = master // 或者连接到从数据库
|
||||
return
|
||||
|
||||
@@ -487,6 +487,8 @@ a.WithSession(Map{"admin_id": int64(1)}).
|
||||
|
||||
**省略错误消息(第三参数)**:`a.Post("请先登录", 2)` — 当 `status != 0` 且未传第三参数时,框架自动用 `desc`(第一参数)作为期望的错误消息。适合 desc 与实际 msg 完全一致的场景。
|
||||
|
||||
**跳过错误消息断言(AnyMsg)**:`a.AnyMsg().Post("无效授权码", 1)` — 当 `status != 0` 但错误 msg 含动态内容(网络拨号错误、第三方 API 回传文案等)无法全等断言时,用 `AnyMsg()` 仅断言 status、跳过 msg 校验(同时也不再用 desc 兜底匹配)。`Api` 与 `ApiCase` 均可链式调用,如 `a.JSON(...).AnyMsg().Post(...)`。样例见 `example/app/expect_demo_test.go` 的 `error_demo`。
|
||||
|
||||
**省略 result 结构校验(expect)**:`a.Post("创建成功", 0)` — 仅断言 `status=0`,不校验 result 的内容和结构。适合无需关注返回结构的场景,也适用于非 JSON 响应(二进制文件、纯文本等),此时在 Verify 中通过 `GetRawBody()` 做内容校验。
|
||||
|
||||
**省略 Verify**:不设置 Verify 回调时,不会执行响应值断言和数据库状态校验。适合纯查询、无副作用的接口。
|
||||
|
||||
@@ -146,6 +146,10 @@ var ExpectDemoTest = CtrTest{
|
||||
// ======== 第一步:错误用例(先行) ========
|
||||
a.Form(Map{"name": ""}).Post("名称为空-缺少必填字段", 3, "名称不能为空")
|
||||
|
||||
// AnyMsg:跳过错误 msg 断言(仅断言 status)。用于响应 msg 含网络错误等
|
||||
// 动态内容的用例——desc 与实际 msg 不一致时,无 AnyMsg 会因 desc 兜底匹配而失败
|
||||
a.Form(Map{"name": ""}).AnyMsg().Post("AnyMsg跳过msg断言-desc与实际msg不一致", 3)
|
||||
|
||||
// ======== 第二步:正确请求 + 结构校验 + Verify 值断言 ========
|
||||
a.Form(Map{"name": "测试商品"}).
|
||||
Verify(func(a *Api) error {
|
||||
|
||||
+15
-1
@@ -189,6 +189,13 @@ func (a *Api) Note(note string) *ApiCase {
|
||||
return c
|
||||
}
|
||||
|
||||
// AnyMsg 跳过错误 msg 断言(仅断言 status),用于响应 msg 含网络错误等动态内容的用例
|
||||
func (a *Api) AnyMsg() *ApiCase {
|
||||
c := a.newCase()
|
||||
c.skipMsg = true
|
||||
return c
|
||||
}
|
||||
|
||||
// DB 获取数据库实例(在测试事务内)
|
||||
func (a *Api) DB() *HoTimeDB {
|
||||
return &a.app.Db
|
||||
@@ -221,6 +228,7 @@ type ApiCase struct {
|
||||
note string
|
||||
verifyFn func(a *Api) error
|
||||
bindCase string // 绑定的单接口验收用例名(FromCase)
|
||||
skipMsg bool // AnyMsg:跳过错误 msg 断言(用于网络错误等动态消息)
|
||||
}
|
||||
|
||||
// FromCase 在已有 ApiCase 上绑定验收用例名(若尚未加载模板则加载)
|
||||
@@ -300,6 +308,12 @@ func (c *ApiCase) Note(note string) *ApiCase {
|
||||
return c
|
||||
}
|
||||
|
||||
// AnyMsg 跳过错误 msg 断言(仅断言 status),可继续链式
|
||||
func (c *ApiCase) AnyMsg() *ApiCase {
|
||||
c.skipMsg = true
|
||||
return c
|
||||
}
|
||||
|
||||
// Verify 设置请求后的自定义校验函数(如查库验证数据状态)
|
||||
// 函数返回 nil 表示校验通过,返回 error 则用例失败并记录错误原因
|
||||
func (c *ApiCase) Verify(fn func(a *Api) error) *ApiCase {
|
||||
@@ -343,7 +357,7 @@ func (c *ApiCase) execute(method, desc string, expectStatus int, expectArgs ...i
|
||||
hasExpectResult = true
|
||||
}
|
||||
}
|
||||
if expectStatus != 0 && expectMsg == "" {
|
||||
if expectStatus != 0 && expectMsg == "" && !c.skipMsg {
|
||||
expectMsg = desc
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user