3.7 高级测试技术
最后更新于:2022-04-01 22:28:24
# 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) 所示。
![red to green](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-11_5732bcf99ed9c.png)图 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) 中的内容。
![file navigator gear icon](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-11_5732bcf9bde77.png)图 3.9:文件浏览器中的齿轮图标(不太好找)![show hidden files](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-11_5732bcf9da252.png)图 3.10:显示隐藏文件![gitignore](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-11_5732bcfa01158.png)图 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)。
';