Revel测试套件必须嵌入revel.TestSuite,否则无法调用t.Get()等封装方法;需启用testrunner模块、正确声明指针接收器、在Before()中初始化资源,并手动清理数据库以保障测试隔离性。

Revel测试套件必须嵌入 revel.TestSuite
不继承这个结构体,就调用不了 t.Get()、t.PostForm() 这类封装好的请求方法。它内部已经初始化了HTTP客户端、自动处理了session和cookie,还把响应体读进 t.ResponseBody 里——省掉手动管理连接和IO的麻烦。
常见错误是直接写个空 struct 或者只嵌入自定义字段,结果运行时 panic:「nil pointer dereference」。因为 revel.TestSuite 的字段(比如 Client)在 Before() 里才被赋值,没嵌入就等于没初始化。
- 必须用指针接收器声明方法:
func (t *AppTest) TestSomething(),否则t.Client是副本,Before()里改的不会生效 -
Before()和After()不是必须的,但建议用来清理数据库或重置 mock 状态 - 测试方法名必须以
Test开头,且首字母大写,否则go test不会识别
路由与模块配置必须启用 testrunner
Revel 的测试不是纯内存模拟,它需要启动一个轻量级测试服务器来接收请求。这个能力由 testrunner 模块提供,但默认不激活。
如果跳过这步,t.Get("/") 会一直超时或返回 404,因为实际没路由注册。错误现象很隐蔽:测试进程卡住、无日志、无 panic,只等 timeout 后失败。
- 在
app.conf中加一行:module.testrunner = github.com/revel/revel/modules/testrunner - 在
routes文件末尾加:module:testrunner - Linux/macOS 下确保
GO111MODULE=on,否则go get可能拉不到 testrunner 的依赖
断言失败时 panic 不会中断整个测试套件
Revel 的 t.AssertOk()、t.AssertContentType() 内部用的是 panic(),但被框架捕获后只标记当前用例失败,继续跑下一个。这点和标准 testing.T 的 t.Fatal() 行为不同。
所以你可能看到终端输出一堆「PANIC」但测试进程还在跑——这不是 bug,是设计如此。但如果在 Before() 里写了未捕获的 panic(比如空指针解引用),整个套件就会中止。
- 自定义断言推荐用
t.Assertf(exp, "msg: %v", val),比裸assert更易定位 - 检查 JSON 响应时别直接用
strings.Contains(string(t.ResponseBody), "token"),应该先json.Unmarshal再 assert 字段,避免字符串误匹配 -
t.AssertOk()只检查状态码 == 200,不校验 body;要验证内容必须手动读t.ResponseBody或用第三方库解析
测试数据隔离靠 RefreshDatabase 不够,得手动清理
Revel 本身不提供类似 Laravel 的 RefreshDatabase trait。如果你的接口操作数据库,多个测试用例共享同一张表,很容易出现「上一个测试插入了用户A,下一个测试查不到用户B」这类干扰。
最稳妥的做法是在每个测试开头清空相关表,或用事务 + rollback。不要依赖测试顺序,也不要假设数据库是空的。
- 在
Before()里执行sql.Exec("DELETE FROM users"),注意不同数据库语法差异(PostgreSQL 需加TRUNCATE) - 避免在
Test方法里用defer清理,因为 panic 时 defer 不一定执行 - 如果用了 ORM,确认其测试模式是否开启事务回滚;没开的话,单测之间必然污染
testrunner 模块是否加载、数据库是否干净、路由是否真正注册,这三个点任何一个出问题,表现都是“测试不报错但逻辑没走通”。建议第一次跑通后,立刻删掉一个路由再跑,确认它真能报 404,而不是静默失败。


















