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](http://gorevel.cn/docs/docs/godoc/tests.html)
所有的請求方法類似:
1. 接受一個路徑 (比如?`/users/`)
2. 向應用程序服務器發出一個請求
3. 存儲?`Response`?中的成員
4. 讀取完整的響應到`ResponseBody`?成員中
如果開發人員希望使用一個定制的HTTP客戶端,而不是默認的[http.DefaultClient](http://golang.org/pkg/net/http/#pkg-variables),應當在`Before()`?方法之前替換它。
斷言失敗后,會拋出恐慌并被測試工具捕獲,并將錯誤列出。
## 運行測試套件
為了運行測試,`testrunner`?模塊必須被激活。需要在?`app.conf`文件中配置:
~~~
module.testrunner = github.com/revel/revel/modules/testrunner
~~~
您還必須導入測試模塊的路由,在你的?`routes`?文件中加入下面的內容:
~~~
module:testrunner
~~~
配置完后,測試就可以交互或非交互方式運行。
### 運行交互式測試
要利用 Revel 的熱編譯功能,交互式測試運行提供了快速編輯刷新周期。
例如,開發人員從瀏覽器中訪問?`/@tests`:

然后,增加一個測試方法:
~~~
func (t AppTest) TestSomethingImportant() {
t.Get("/")
t.AssertOk()
t.AssertContentType("text/xml")
}
~~~
然后,刷新瀏覽器,看看新的測試:

運行測試:

嗯哼,,,行不通哦,,,修改代碼使用“text/html” 替換 “text/xml”類型。
~~~
t.AssertContentType("text/html")
~~~
然后,重新運行測試:

成功啦!
### 運行非交互式測試
Revel?[命令行工具](http://gorevel.cn/docs/manual/tool.html)?提供了一個?`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.passed`?或?`result.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`