revel提供了一个测试框架来方便的为自己的程序编写功能测试用例。
默认创建的应用骨架附带一个简单的测试用例,这里将它作为起点
##概览
测试保存在`tests`目录:
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")
}
这个示例做了如下事情:
1、一个测试用例可以是任意一个嵌入了`revel.TestSuite`的struct
2、如果存在`Before()`与`After()`方法,那么它将在每个测试用例的前后被调用。
3、`revel.TestSuite`为请求和响应的断言提供了辅助工具。
4、一个失败的断言讲抛出一个`panic`,会由测试工具进行捕获
可以在这两个地方使用测试工具:
1、交互模式,在浏览器中使用,开发阶段下非常有用
2、非交换模式,在命令行中使用,对于集成持续构建非常有用
##开发一个测试用例
自己创建一个测试用例,只需要将`revel.TestSuite`嵌入struct即可,它会提供一个HTTP客户端和一些用于请求的辅助性工具。
// 一些请求方法
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{})
所有的请求都有类似的功能:
1、它们接收一个路径(如/users)
2、它们想服务器发送一个请求
3、它们在`Response`中存储一个响应
4、他们在`ResponseBody`中读取全部的响应体
如果开发者想使用一个自定义的HTTP客户端,而不想用默认的`http.DefaultClient`,那么只需要在`Before()`方法中替换。
所有不通过的断言都将抛出一个`panic`,所有被测试工具捕获的`panic`都被当作错误。
##运行一个测试用例
如果要运行测试用力,那么`testrunner`模块必须被激活。编辑`app.conf`激活测试框架:
module.testrunner = github.com/robfig/revel/modules/testrunner
并且还得在`routes`路由文件添加测试的路由:
module:testrunner
这样,测试用例才能在交互模式与非交互模式下运行。
**交互模式下运行测试用例**
利用revel的代码热编译功能,测试框架提供了一个快速的 更改-刷新 周期
例如在开发阶段,在浏览器中加载`/@tests`页面
![test](http://robfig.github.io/revel/img/Tests1.png "test")
然后,添加一个测试方法:
func (t AppTest) TestSomethingImportant() {
t.Get("/")
t.AssertOk()
t.AssertContentType("text/xml")
}
接着刷新浏览器页面就会看到新增加的测试方法:
![test](http://robfig.github.io/revel/img/Tests2.png "test")
运行测试用例:
![test](http://robfig.github.io/revel/img/Tests3.png "test")
额。。它没有运行,这里所期望的内容类型是`text/html`而不是`text/xml`,修改它:
t.AssertContentType("text/html")
然后重新运行:
![test](http://robfig.github.io/revel/img/Tests4.png "test")
成功!
**非交互模式下运行测试用例**
revel工具提供了`test`选项在命令行下运行测试用例,例如:
$ revel test github.com/robfig/revel/samples/booking dev
~
~ revel! http://robfig.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/robfig/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/robfig/revel/samples/booking dev ApplicationTest
$ revel test github.com/robfig/revel/samples/booking dev ApplicationTest.TestThatIndexPageWorks
命令行下,测试用例只会简单的显示`PASSED/FAILED`来表示测试是否通过,但测试工具会在文件系统生成详细的测试报告:
$ cd src/github.com/robfig/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.passed`或`result.failed`的生成,取决于整个测试否通过
对于连续构建,这里有两点建议:
1、检查返回码,0表示成功,其它则表示失败
2、检查文件,如果`result.passed`文件存在,则表示测试通过,反之则不通过
##实现说明
revel做了哪些事:
1、扫描测试源码用来传递给嵌入的测试用例
2、将`revel.TestSuites`变量的那些类型的列表设置进生成的`main.go`文件中
3、根据反射来获取所有以`Test`开头`TestSuite`类型的方法,然后运行测试代码
4、捕获所有的错误,并显示出来
测试代码只有在`testrunner`模块启用的情况下才会被建立。
##开发相关
测试框架可以有下面的改进:
1、用于将测试数据存入存储区域的装置
2、`Loggers`输出文件(代替stderr/stdout)也应该重定向至`test-results/app.log`