Ruby RSpec 全套测试环境搭建从零入门

hfpp2012 · 发布于 2016年6月07日 · 最后由 hfpp2012 回复于 2016年6月08日 · 2259 次阅读
16154

1. 缘起

要帮朋友的项目搭一套测试环境,选用了rspec作为测试环境,在搭建的过程中记录整个过程,以后让这种重复的工作简单点,不想又造轮子。

2. 能学到什么

  • 各种rspec测试标配
  • 使用spring加速测试过程,也会和guard结合,加速guard的运行过程
  • 使用guard让文件一保存就自动测试
  • 使用capybara写类似于cucumber的feature测试

3. 安装过程

由于guard的安装需要高版本的ruby支持,建议使用ruby 2.3.0以上。

首先安装rspec这个gem,我们选用的是适合rails项目的rspec-rails这个gem。

3.1 rspec-rails

在Gemfile文件里加入下面这几行:

group :development, :test do
  gem 'rspec-rails', '~> 3.4'
end

执行bundle install安装。

装好之后,执行下面的两行命令,生成必要的配置文件。

rails generate rspec:install
bundle binstubs rspec-core
3.2 factory_girl_rails

factory_girl_rails是一个代替测试夹具(Fixtures)的工具,用它可以在测试的时候造一些实例。

group :development, :test do
  gem 'factory_girl_rails'
end

执行bundle install安装。

接下来找到spec/rails_helper.rb文件的下面一行,把其删除或注释掉。

config.use_transactional_fixtures = true

并且增加下面一行。

config.include FactoryGirl::Syntax::Methods

再到config/application.rb文件中,添加下面几行:

config.generators do |g|
  g.test_framework :rspec,
    fixtures: true,
    view_specs: false,
    helper_specs: false,
    routing_specs: false,
    request_specs: false
  g.fixture_replacement :factory_girl, dir: 'spec/factories'
end

为了方便后绪的demo测试,现在我们都可以生成一个夹具文件,名字叫users.rb,位于spec/factories

FactoryGirl.define do
  factory :user do
    email                    'user@example.com'
    username                 'name'
    password                 'password'
  end
end
3.3 database_cleaner

database_cleaner是一个自动清除数据库数据的工具,每次运行完测试用例它就会自动清除数据库。

Gemfile中添加下面几行。

group :test do
  gem 'database_cleaner'
end

执行bundle install安装。

然后在spec/rails_helper.rb文件中添加下面几行:

config.before(:suite) do
  DatabaseCleaner.strategy = :transaction
  DatabaseCleaner.clean_with(:truncation)
end

config.around(:each) do |example|
  DatabaseCleaner.cleaning do
    example.run
  end
end

到现在为止,已经能够正常跑测试了,我们来写一个测试案例,让它跑起来。

新建文件spec/models/user_spec.rb,内容如下:

require 'rails_helper'

RSpec.describe User, type: :model do
  let(:user) { create :user }

  describe '#email' do
    context '有效的邮箱' do
      addresses = %w( user@foo.COM A_US-ER@f.b.org frst.lst@foo.jp a+b@baz.cn )
      addresses.each do |valid_address|
        let(:user) { build(:user, email: valid_address) }
        it { expect(user.valid?).to eq true }
      end
    end

    context '空' do
      let(:user) { build(:user, email: '') }
      it { expect(user.valid?).to eq false }
    end

    context '错误邮箱格式' do
      addresses = %w{ invalid_email_format 123 $$$ () ☃ bla@bla. }
      addresses.each do |invalid_address|
        let(:user) { build(:user, email: invalid_address) }
        it { expect(user.valid?).to eq false }
      end
    end

    context '重复' do
      let(:user_with_same_username) { build :user, username: user.username }
      it { expect(user_with_same_username.valid?).to eq false }
    end
  end
end

可以使用bundle exec rspec spec/models/user_spec.rb来测试这个刚才写的测试案例。

你会发现运行起来还是比较慢的。

接下来我们使用spring来加速测试的运行。

3.4 spring-commands-rspec

spring是rails默认就有的,而spring-commands-rspec是让springrspec结合起来。

Gemfile中添加下面这行:

gem 'spring-commands-rspec', group: :development

接着执行bundle exec spring binstub rspec这条指令。

现在我们就可以使用bundle exec spring rspec来加速测试的运行了。

3.5 guard

guard是一个让你一修改测试文件,就自动跑测试的工具。

需要注意的是guard需要高版本的ruby支持,目前为止,它官方写的是至少需要ruby 2.2.5以上

Gemfile中添加下面几行:

group :development do
  gem 'guard'
  gem 'guard-rspec', require: false
  gem 'guard-bundler', require: false
end

至于guard-rspecguard-bundlerguard的两个插件,是分别让guardrspec还有bundler结合。

分别执行下面几行指令。

bundle exec guard init
bundle exec guard init rspec
bundle exec guard init bundler

接着找到Guardfile文件,找到第一行未注释的代码,修改成类似下面这样。

guard :rspec, cmd: 'spring rspec', all_on_start: true do

现在可以运行bundle exec guard执行测试了,也能监控文件的更改,测试文件一旦有修改,也会马上运行测试。

springguard结合之后,运行测试是很快的。

3.6 capybara

capybara是一个可以编写feature测试的工具,它可以编写BDD测试、模拟浏览器点击,填充表单的测试功能。

Gemfile中添加下面一行:

group :development, :test do
  gem 'capybara'
end

执行bundle install安装。

安装完之后,找到spec/rails_helper.rb文件添加下面一行:

require 'capybara/rails'

现在我们可以编写一个feature测试案例。

文件名为spec/features/login_spec.rb

require 'rails_helper'

describe '登录功能', type: :feature do
  let(:user) { create(:user) }

  before { visit new_user_session_path }