# 3.7 高級測試技術
這一節選讀,介紹本書配套視頻中使用的測試設置,包含三方面內容:增強版通過和失敗報告程序([3.7.1 節](#minitest-reporters));過濾測試失敗消息中調用跟蹤的方法([3.7.2 節](#backtrace-silencer));一個自動測試運行程序,檢測到文件有變化后自動運行相應的測試([3.7.3 節](#automated-tests-with-guard))。這一節使用的代碼相對高級,放在這里只是為了查閱方便,現在并不期望你能理解。
這一節應該在主分支中修改:
```
$ git checkout master
```
## 3.7.1 MiniTest 報告程序
為了讓 Rails 中的測試適時顯示紅色和綠色,我建議你在測試輔助文件中加入[代碼清單 3.40](#listing-minitest-reporters) 中的內容,[[15](#fn-15)]充分利用[代碼清單 3.2](#listing-gemfile-sample-app) 中的 [minitest-reporters](https://github.com/kern/minitest-reporters) gem。
##### 代碼清單 3.40:配置測試,顯示紅色和綠色
test/test_helper.rb
```
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require "minitest/reporters" Minitest::Reporters.use!
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical
# order.
fixtures :all
# Add more helper methods to be used by all tests here...
end
```
修改后,在云端 IDE 中顯示的效果如[圖 3.8](#fig-red-to-green) 所示。
圖 3.8:在云端 IDE 中測試由紅變綠
## 3.7.2 調用跟蹤靜默程序
如果有錯誤,或者測試失敗,測試運行程序會顯示調用跟蹤,從失敗的測試開始一直追溯到應用代碼。調用跟蹤對查找問題來說很有用,但在某些系統中(包括云端 IDE),會一直追溯到應用的代碼以及各個 gem(包括 Rails)中,顯示的內容往往很多。如果問題發生在應用代碼中,而不是它的依賴件中,那么內容更多。
我們可以過濾調用追蹤,不顯示不需要的內容。為此,我們要使用[代碼清單 3.2](#listing-gemfile-sample-app) 中的 [mini_backtrace](https://github.com/metaskills/mini_backtrace) gem,然后再設置靜默程序。在云端 IDE 中,大多數不需要的內容都包含字符串“rvm”(指的是 Ruby Version Manager),所以我建議使用[代碼清單 3.41](#listing-backtrace-silencer) 中的靜默程序把這些內容過濾掉。
##### 代碼清單 3.41:添加調用跟蹤靜默程序,過濾 RVM 相關的內容
config/initializers/backtrace_silencers.rb
```
# Be sure to restart your server when you modify this file.
# You can add backtrace silencers for libraries that you're using but don't
# wish to see in your backtraces.
Rails.backtrace_cleaner.add_silencer { |line| line =~ /rvm/ }
# You can also remove all the silencers if you're trying to debug a problem
# that might stem from framework code.
# Rails.backtrace_cleaner.remove_silencers!
```
如這段代碼中的注釋所說,添加靜默程序后要重啟本地服務器。
## 3.7.3 使用 Guard 自動測試
使用 `rake test` 命令有一點很煩人,總是要切換到命令行然后手動運行測試。為了避免這種不便,我們可以使用 [Guard](https://github.com/guard/guard) 自動運行測試。Guard 會監視文件系統的變動,假如你修改了 `static_pages_controller_test.rb`,那么 Guard 只會運行這個文件中的測試。而且,我們還可以配置 Guard,讓它在 `home.html.erb` 文件被修改后,也自動運行 `static_pages_controller_test.rb`。
[代碼清單 3.2](#listing-gemfile-sample-app) 中已經包含了 `guard` gem,所以我們只需初始化即可:
```
$ bundle exec guard init
Writing new Guardfile to /home/ubuntu/workspace/sample_app/Guardfile
00:51:32 - INFO - minitest guard added to Guardfile, feel free to edit it
```
然后再編輯生成的 `Guardfile` 文件,讓 Guard 在集成測試和視圖發生變化后運行正確的測試,如[代碼清單 3.42](#listing-guardfile) 所示。(這個文件的內容很長,而且需要高級知識,所以我建議直接復制粘貼。)
##### 代碼清單 3.42:修改 `Guardfile`
```
# Defines the matching rules for Guard.
guard :minitest, spring: true, all_on_start: false do
watch(%r{^test/(.*)/?(.*)_test\.rb$})
watch('test/test_helper.rb') { 'test' }
watch('config/routes.rb') { integration_tests }
watch(%r{^app/models/(.*?)\.rb$}) do |matches|
"test/models/#{matches[1]}_test.rb"
end
watch(%r{^app/controllers/(.*?)_controller\.rb$}) do |matches|
resource_tests(matches[1])
end
watch(%r{^app/views/([^/]*?)/.*\.html\.erb$}) do |matches|
["test/controllers/#{matches[1]}_controller_test.rb"] +
integration_tests(matches[1])
end
watch(%r{^app/helpers/(.*?)_helper\.rb$}) do |matches|
integration_tests(matches[1])
end
watch('app/views/layouts/application.html.erb') do
'test/integration/site_layout_test.rb'
end
watch('app/helpers/sessions_helper.rb') do
integration_tests << 'test/helpers/sessions_helper_test.rb'
end
watch('app/controllers/sessions_controller.rb') do
['test/controllers/sessions_controller_test.rb',
'test/integration/users_login_test.rb']
end
watch('app/models/micropost.rb') do
['test/models/micropost_test.rb', 'test/models/user_test.rb']
end
watch(%r{app/views/users/*}) do
resource_tests('users') +
['test/integration/microposts_interface_test.rb']
end
end
# Returns the integration tests corresponding to the given resource.
def integration_tests(resource = :all)
if resource == :all
Dir["test/integration/*"]
else
Dir["test/integration/#{resource}_*.rb"]
end
end
# Returns the controller tests corresponding to the given resource.
def controller_test(resource)
"test/controllers/#{resource}_controller_test.rb"
end
# Returns all tests for the given resource.
def resource_tests(resource)
integration_tests(resource) << controller_test(resource)
end
```
下面這行
```
guard :minitest, spring: true, all_on_start: false do
```
會讓 Guard 使用 Rails 提供的 Spring 服務器減少加載時間,而且啟動時不運行整個測試組件。
使用 Guard 時,為了避免 Spring 和 Git 發生沖突,應該把 `spring/` 文件夾加到 `.gitignore` 文件中,讓 Git 忽略這個文件夾。在云端 IDE 中要這么做:
* 點擊文件瀏覽器右上角的齒輪圖標,如[圖 3.9](#fig-file-navigator-gear-icon) 所示;
* 選擇“Show hidden files”(顯示隱藏文件),讓 `.gitignore` 文件出現在應用的根目錄中,如[圖 3.10](#fig-show-hidden-files) 所示;
* 雙擊打開 `.gitignore` 文件([圖 3.11](#fig-gitignore)),寫入[代碼清單 3.43](#listing-gitignore-spring) 中的內容。
圖 3.9:文件瀏覽器中的齒輪圖標(不太好找)圖 3.10:顯示隱藏文件圖 3.11:通常隱藏的 `.gitignore` 文件出現了
##### 代碼清單 3.43:把 Spring 添加到 `.gitignore` 文件中
```
# See https://help.github.com/articles/ignoring-files for more about ignoring
# files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile '~/.gitignore_global'
# Ignore bundler config.
/.bundle
# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3-journal
# Ignore all logfiles and tempfiles.
/log/*.log
/tmp
# Ignore Spring files. /spring/*.pid
```
寫作本書時,Spring 服務器還有點兒怪異,有時 Spring 進程會不斷拖慢測試的運行速度。如果你發現測試變得異常緩慢,最好查看系統進程([旁注 3.4](#aside-processes)),如果需要,關掉 Spring 進程。
##### 旁注 3.4:Unix 進程
在 Unix 類系統中,例如 Linux 和 OS X,用戶和系統執行的任務都在包裝良好的容器中,這個容器叫“進程”(process)。若想查看系統中的所有進程,可以執行 `ps` 命令,并指定 `aux` 參數:
```
$ ps aux
```
若想過濾輸出的進程,可以使用 Unix 管道操作(`|`)把 `ps` 命令的結果傳給 `grep`,進行模式匹配:
```
$ ps aux | grep spring
ubuntu 12241 0.3 0.5 589960 178416 ? Ssl Sep20 1:46
spring app | sample_app | started 7 hours ago
```
顯示的結果中有進程的部分詳細信息,其中最重要的是第一個數字,即進程的 ID,簡稱 pid。若要終止不想要的進程,可以使用 `kill` 命令,向指定的 pid 發送 kill 信號([恰巧是 9](https://en.wikipedia.org/wiki/Unix_signal#List_of_signals)):
```
$ kill -15 12241
```
關閉單個進程,例如不再使用的 Rails 服務器進程(可執行 `ps aux | grep server` 命令找到 pid),我推薦使用這種方法。不過,有時最好能批量關閉進程名種包含特定文本的進程,例如關閉系統中所有的 `spring` 進程。針對 Spring,首先應該嘗試使用 `spring` 命令關閉進程:
```
$ spring stop
```
不過,有時這么做沒用,那么可以使用 `pkill` 命令關閉所有名為“spring”的進程:
```
$ pkill -15 -f spring
```
只要發現表現異常,或者進程靜止了,最好執行 `ps aux` 命令看看怎么回事,然后再執行 `kill -15 <pid>` 或 `pkill -15 -f <name>` 命令關閉進程。
配置好 Guard 之后,應該打開一個新終端窗口(和 [1.3.2 節](chapter1.html#rails-server)啟動 Rails 服務器的做法一樣),在其中執行下述命令:
```
$ bundle exec guard
```
[代碼清單 3.42](#listing-guardfile) 中的規則針對本書做了優化,例如,修改控制器后會自動運行集成測試。如果想運行所有測試,在 `guard>` 終端中按回車鍵。(有時會看到一個錯誤,說連接 Spring 服務器失敗。再次按回車鍵就能解決這個問題。)
若想退出 Guard,按 Ctrl-D 鍵。如果想為 Guard 添加其他的匹配器,參照[代碼清單 3.43](#listing-gitignore-spring),[Guard 的說明文件](https://github.com/guard/guard#readme)和[維基](https://github.com/guard/guard/wiki)。
- Ruby on Rails 教程
- 致中國讀者
- 序
- 致謝
- 作者譯者簡介
- 版權和代碼授權協議
- 第 1 章 從零開始,完成一次部署
- 1.1 簡介
- 1.2 搭建環境
- 1.3 第一個應用
- 1.4 使用 Git 做版本控制
- 1.5 部署
- 1.6 小結
- 1.7 練習
- 第 2 章 玩具應用
- 2.1 規劃應用
- 2.2 用戶資源
- 2.3 微博資源
- 2.4 小結
- 2.5 練習
- 第 3 章 基本靜態的頁面
- 3.1 創建演示應用
- 3.2 靜態頁面
- 3.3 開始測試
- 3.4 有點動態內容的頁面
- 3.5 小結
- 3.6 練習
- 3.7 高級測試技術
- 第 4 章 Rails 背后的 Ruby
- 4.1 導言
- 4.2 字符串和方法
- 4.3 其他數據類型
- 4.4 Ruby 類
- 4.5 小結
- 4.6 練習
- 第 5 章 完善布局
- 5.1 添加一些結構
- 5.2 Sass 和 Asset Pipeline
- 5.3 布局中的鏈接
- 5.4 用戶注冊:第一步
- 5.5 小結
- 5.6 練習
- 第 6 章 用戶模型
- 6.1 用戶模型
- 6.2 用戶數據驗證
- 6.3 添加安全密碼
- 6.4 小結
- 6.5 練習
- 第 7 章 注冊
- 7.1 顯示用戶的信息
- 7.2 注冊表單
- 7.3 注冊失敗
- 7.4 注冊成功
- 7.5 專業部署方案
- 7.6 小結
- 7.7 練習
- 第 8 章 登錄和退出
- 8.1 會話
- 8.2 登錄
- 8.3 退出
- 8.4 記住我
- 8.5 小結
- 8.6 練習
- 第 9 章 更新,顯示和刪除用戶
- 9.1 更新用戶
- 9.2 權限系統
- 9.3 列出所有用戶
- 9.4 刪除用戶
- 9.5 小結
- 9.6 練習
- 第 10 章 賬戶激活和密碼重設
- 10.1 賬戶激活
- 10.2 密碼重設
- 10.3 在生產環境中發送郵件
- 10.4 小結
- 10.5 練習
- 10.6 證明超時失效的比較算式
- 第 11 章 用戶的微博
- 11.1 微博模型
- 11.2 顯示微博
- 11.3 微博相關的操作
- 11.4 微博中的圖片
- 11.5 小結
- 11.6 練習
- 第 12 章 關注用戶
- 12.1 “關系”模型
- 12.2 關注用戶的網頁界面
- 12.3 動態流
- 12.4 小結
- 12.5 練習