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

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

概要

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

corp/myapp
	app/
	conf/
	public/
	tests/    <----

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

type AppTest struct {
  revel.TestSuite
}

func (t *AppTest) Before() {
	println("Set up")
}

func (t *AppTest) TestThatIndexPageWorks() {
	t.Get("/")
	t.AssertOk()
	t.AssertContentType("text/html")
}

func (t *AppTest) After() {
	println("Tear down")
}

上面的例子中展示了:

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

你可以用两种方式运行这个测试:

  • 交互方式,Web浏览器,开发过程中非常有用。
  • 非交互方式,命令行,对于持续构建整合有用。

开发一个测试套件

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

type TestSuite struct {
	Client       *http.Client
	Response     *http.Response
	ResponseBody []byte
}

// 一些请求方法
func (t *TestSuite) Get(path string)
func (t *TestSuite) Post(path string, contentType string, reader io.Reader)
func (t *TestSuite) PostForm(path string, data url.Values)
func (t *TestSuite) MakeRequest(req *http.Request)

// 一些断言方法
func (t *TestSuite) AssertOk()
func (t *TestSuite) AssertContentType(contentType string)
func (t *TestSuite) Assert(exp bool)
func (t *TestSuite) Assertf(exp bool, formatStr string, args ...interface{})

参考godoc

所有的请求方法类似:

  1. 接受一个路径 (比如 /users/)
  2. 向应用程序服务器发出一个请求
  3. 存储 Response 中的成员
  4. 读取完整的响应到ResponseBody 成员中

如果开发人员希望使用一个定制的HTTP客户端,而不是默认的http.DefaultClient,应当在Before() 方法之前替换它。

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

运行测试套件

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

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

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

module:testrunner

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

运行交互式测试

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

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

Test Runner interface

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

func (t AppTest) TestSomethingImportant() {
	t.Get("/")
	t.AssertOk()
	t.AssertContentType("text/xml")
}

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

Test Runner interface

运行测试:

Test Runner interface

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

	t.AssertContentType("text/html")

然后,重新运行测试:

Test Runner interface

成功啦!

运行非交互式测试

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

下面是一个示例会话:

$ revel test github.com/revel/revel/samples/booking dev
~
~ revel! http://revel.github.com/revel
~
INFO  2012/11/09 19:21:02 revel.go:237: Loaded module testrunner
Open DB
Listening on port 9000...
INFO  2012/11/09 19:21:06 test.go:95: Testing Booking example (github.com/revel/revel/samples/booking) in dev mode
Go to /@tests to run the tests.

1 test suite to run.

AppTest                 PASSED        0s

All Tests Passed.

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

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

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

$ cd src/github.com/revel/revel/samples/booking
$ find test-results
test-results
test-results/app.log
test-results/AppTest.passed.html
test-results/result.passed

它写三点不同:

  1. 应用程序的标准输出和标准错误重定向到 app.log
  2. 每个测试套件有一个HTML文件被写入,说明测试通过或失败。
  3. 无论 result.passedresult.failed 被写入, 这取决于整体的成功。

对于整合持续构建测试,有两点建议:

  1. 检查返回码,0代表测试成功,否则为非0值。
  2. 测试运行后要求存在 result.success, 或禁止 result.failed存在。

注意事项

Revel 做了什么:

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

testrunner 模块激活后,测试代码才会被构建。

开发计划

改进测试框架:

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