测试

Revel提供了一个测试框架,可以很容易地编写和运行针对您的应用程序的功能测试。

应用程序带有一个简单的测试骨架以便快速上手测试。

概要

测试代码保存在测试目录中:

  1. corp/myapp
  2. app/
  3. conf/
  4. public/
  5. tests/ <----

一个简单的测试如下所示:

  1. type AppTest struct {
  2. revel.TestSuite
  3. }
  4.  
  5. func (t *AppTest) Before() {
  6. println("Set up")
  7. }
  8.  
  9. func (t *AppTest) TestThatIndexPageWorks() {
  10. t.Get("/")
  11. t.AssertOk()
  12. t.AssertContentType("text/html")
  13. }
  14.  
  15. func (t *AppTest) After() {
  16. println("Tear down")
  17. }

上面的例子中展示了:

  • 测试套件是嵌入revel.TestSuite的一个struct
  • Before()After() 在每个测试方法之前和之后被调用,如果有的话。
  • revel.TestSuite 帮助发出请求到你的应用程序,和对响应的断言。
  • 如果一个断言失败,产生了恐慌,将被测试工具捕获。
    你可以用两种方式运行这个测试:

  • 交互方式,Web浏览器,开发过程中非常有用。

  • 非交互方式,命令行,对于持续构建整合有用。

开发一个测试套件

要创建自己的测试套件,需要定义一个嵌入了revel.TestSuite类型的struct, 它提供了一个HTTP客户端和一些辅助方法发出请求到应用程序。

  1. type TestSuite struct {
  2. Client *http.Client
  3. Response *http.Response
  4. ResponseBody []byte
  5. }
  6.  
  7. // 一些请求方法
  8. func (t *TestSuite) Get(path string)
  9. func (t *TestSuite) Post(path string, contentType string, reader io.Reader)
  10. func (t *TestSuite) PostForm(path string, data url.Values)
  11. func (t *TestSuite) MakeRequest(req *http.Request)
  12.  
  13. // 一些断言方法
  14. func (t *TestSuite) AssertOk()
  15. func (t *TestSuite) AssertContentType(contentType string)
  16. func (t *TestSuite) Assert(exp bool)
  17. func (t *TestSuite) Assertf(exp bool, formatStr string, args ...interface{})

参考godoc

所有的请求方法类似:

  • 接受一个路径 (比如 /users/)
  • 向应用程序服务器发出一个请求
  • 存储 Response 中的成员
  • 读取完整的响应到ResponseBody 成员中
    如果开发人员希望使用一个定制的HTTP客户端,而不是默认的http.DefaultClient,应当在Before() 方法之前替换它。

断言失败后,会抛出恐慌并被测试工具捕获,并将错误列出。

运行测试套件

为了运行测试,testrunner 模块必须被激活。需要在 app.conf文件中配置:

  1. module.testrunner = github.com/revel/revel/modules/testrunner

您还必须导入测试模块的路由,在你的 routes 文件中加入下面的内容:

  1. module:testrunner

配置完后,测试就可以交互或非交互方式运行。

运行交互式测试

要利用 Revel 的热编译功能,交互式测试运行提供了快速编辑刷新周期。

例如,开发人员从浏览器中访问 /@tests:

Test Runner interface

然后,增加一个测试方法:

  1. func (t AppTest) TestSomethingImportant() {
  2. t.Get("/")
  3. t.AssertOk()
  4. t.AssertContentType("text/xml")
  5. }

然后,刷新浏览器,看看新的测试:

Test Runner interface

运行测试:

Test Runner interface

嗯哼,,,行不通哦,,,修改代码使用“text/html” 替换 “text/xml”类型。

  1. t.AssertContentType("text/html")

然后,重新运行测试:

Test Runner interface

成功啦!

运行非交互式测试

Revel 命令行工具 提供了一个 test 命令,允许在命令行中运行测试。

下面是一个示例会话:

  1. $ revel test github.com/revel/revel/samples/booking dev
  2. ~
  3. ~ revel! http://revel.github.com/revel
  4. ~
  5. INFO 2012/11/09 19:21:02 revel.go:237: Loaded module testrunner
  6. Open DB
  7. Listening on port 9000...
  8. INFO 2012/11/09 19:21:06 test.go:95: Testing Booking example (github.com/revel/revel/samples/booking) in dev mode
  9. Go to /@tests to run the tests.
  10. 1 test suite to run.
  11. AppTest PASSED 0s
  12. All Tests Passed.

您还可以运行单个测试套件,或套件内的方法,用句点分隔参数:

  1. $ revel test github.com/revel/revel/samples/booking dev ApplicationTest
  2. $ revel test github.com/revel/revel/samples/booking dev ApplicationTest.TestThatIndexPageWorks

在控制台测试套件只有一个简单的合格/不合格显示。更详细的结果写入到文件系统:

  1. $ cd src/github.com/revel/revel/samples/booking
  2. $ find test-results
  3. test-results
  4. test-results/app.log
  5. test-results/AppTest.passed.html
  6. test-results/result.passed

它写三点不同:

  • 应用程序的标准输出和标准错误重定向到 app.log
  • 每个测试套件有一个HTML文件被写入,说明测试通过或失败。
  • 无论 result.passedresult.failed 被写入, 这取决于整体的成功。
    对于整合持续构建测试,有两点建议:

  • 检查返回码,0代表测试成功,否则为非0值。

  • 测试运行后要求存在 result.success, 或禁止 result.failed存在。

注意事项

Revel 做了什么:

  • 扫描嵌入TestSuite类型 (transitively) 的源代码
  • 在生成的 main.go 文件中,为 revel.TestSuites 类型的变量设置一个列表
  • 按要求,使用反射来查找所有以“Test”开头的TestSuite类型的方法,并调用它们来运行测试。
  • 从错误或失败的断言捕获恐慌,显示错误。
    testrunner 模块激活后,测试代码才会被构建。

开发计划

改进测试框架:

  • 固定存储测试数据。
  • 日志写入到一个文件中(而不是 stderr / stdout)也应该被重定向到 test-results/app.log