[WIP] Rails rspec document

创建: 2019/10/09

更新: 2019/11/30 补充before的参数 all/each

更新: 2020/4/22 补充多个before的运行顺序, 补充after, around

https://rspec.info/documentation/

安装与使用
 Gemfile

 

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

 

 

 bash  
rails g rspec:install

 

 生成spec文件

 

# RSpec hooks into built-in generators
$ rails generate model user
      invoke  active_record
      create    db/migrate/20181017040312_create_users.rb
      create    app/models/user.rb
      invoke    rspec
      create      spec/models/user_spec.rb

# RSpec also provides its own spec file generators
$ rails generate rspec:model user
      create  spec/models/user_spec.rb

# List all RSpec generators
$ rails generate --help | grep rspec

 

 

 运行

 

# Default: Run all spec files (i.e., those matching spec/**/*_spec.rb)
$ bundle exec rspec

# Run all spec files in a single directory (recursively)
$ bundle exec rspec spec/models

# Run a single spec file
$ bundle exec rspec spec/controllers/accounts_controller_spec.rb

# Run a single example from a spec file (by line number)
$ bundle exec rspec spec/controllers/accounts_controller_spec.rb:8

# See all options for running specs
$ bundle exec rspec --help

 

 ● 避免每次都输入bundle exec

bundle binstubs rspec-core

 

 

   
基本方法
 describe/context 

 给测试增加命名空间 

 ● context主要用于分条件测试,相当于if

RSpec.describe 'sample test' do

    ...
end

 ● 除了最外侧的,其他的可以省略 RSpec. 

RSpec.describe 'sample test' do
    describe 'A' do
        ...
    end
end

 ● 参数除了字符串,也可以是class, 还可以省略

RSpec.describe Sample do
    ...
end

 

 

 xdescribe/xcontext

 跳过测试, 状态为pending

 it/example/specify

 测试的基本单位(example)

 完全同名it/example/specify

RSpec.describe 'sample test' do
    it 'sample it' do
    end
end

 ● it为叶代码块,内部不能有describe/context等

 ● 同一个describe内部也可以有多个it

describe 'sample' do
    it '...' { ... }
    it '...' { ... }
    ...
end

 

 ● 同一个it内可以有多个expect

describe 'sample' do
    it '...' {
        expect(...).to ...
        expect(...).to ...
        expect(...).to ...
    end
end

 ● 不附加代码块默认为pending

   可以用作待完成项目

 xit/xexample/xspecify

 跳过测试, 状态为pending

 except

 描述期待的情况

RSpec.describe 'sample test' do
    it '1+1=2' do
        expect(1+1).to eq 2
    end
end

 

 callback

 

 before/before(:each)

 before(:all)

 

 每次example(it)前都会呼出

 在里面放上测试时通用的数据等

 ● 默认before(:each), 即每个it都会呼出一次。如果只要第一次呼出, 用before(:all)

 ● 内部用实例变量(@...)才能传到it

 ● 嵌套的descirbe/context, 每一个一个都会从根开始呼出before

 ● 多个的话从前往后运行

RSpec.describe Book do
  before do
    @book = Book.create(title: 'a')
    puts "UUID A"
  end
  describe '#title' do
    before { puts "UUID B" }
    it 'title a' do
      expect(@book.title).to eq('a')
    end
  end

  describe '#title_' do
    before { puts "UUID C" }
    it 'title a' do
      expect(@book.title).to eq('a')
    end
  end
end

 结果

UUID A
UUID B
UUID A
UUID C

 

 after  多个的话从后往前运行
 around

 ● 运行顺序

 all before

 around before

 each before

 each after

 around after

 all after

 即before/after(:all) > around > before/after(:each)

 ● 多个around则按先后包裹

 

 let

  参数作为变量使用,值为let代码块的返回值

 lazyLoad   

let(:...) { ... }
let(:...) do ... end

 

 

 let!

 直接评价, 不是lazyload

 和before一样

 subject 

 测试对象为明确的一个(即在一定范围内expect的参数相同)

 测试对象为subject的返回值

subject { ... }
subject do
 ...
end
#
subject { User.name }
subject { 1 }

 

 ● 用了subject则 expect 换成 is_expected 

 

 

 shared_examples

 it_behaves_like 

 定义相同的it,重复利用

shared_examples 'name' do
    it { is_expected.to ... }
end

# 没定义subject的
shared_examples 'name' do
    it { expect(...).to ... }
end

使用

it_behaves_like 'name'

 

 ● 例

RSpec.describe Book do
  let(:book) { Book.create(title: title) }
  subject { book.title }
  shared_examples 'sample' do
    it { is_expected.to eq(title) }
  end
  describe '#title = a' do
    let(:title) { 'a' }
    it_behaves_like 'sample'
  end
  describe '#title = b' do
    let(:title) { 'b'}
    it_behaves_like 'sample'
  end
end

 

 

 shared_context

 include_context

 和上面差不多

 例

RSpec.describe Book do
  let(:book) { Book.create(title: title) }
  subject { book.title }
  shared_examples 'sample' do
    it { is_expected.to eq(title) }
  end
  shared_context 'title = a' do
    let(:title) { 'a' }
  end
  shared_context 'title = b' do
    let(:title) { 'b' }
  end
  context '#title  =  a' do
    include_context 'title = a'
    it_behaves_like 'sample'
  end
  context '#title  =  b' do
    include_context 'title = b'
    it_behaves_like 'sample'
  end
end

 

 

 pending

 用于测试出错,暂时保留

context '#title  =  b' do
  include_context 'title = b'
  pending 'must be error'
  it_behaves_like 'sample'
end

 

 

 skip  跳过测试, 状态仍然为pending
 to, not_to/to_not

 后面指定matcher

 

expect(...).to ...
is_expected.to ...

 

 

 

matcher
 eq   是否相等, ==
 be 

 ● 和 =, >=, <= 配合使用, 比较值大小

 ● 不带符号则相当于equal?, 比较是不是同一个(指针是否相同)

expect(User.first).to be User.first

 

 

 be_xxx 

 ruby/rails的带?的方法去掉问号前面加be_

 (方法必须返回true/false)

 be_truthy / be_falsey

 和be true/false区别

 nil也算falsey

 change + from / to / by  
expect{...}.to change{...}.from(...).to(...)
expect{...}.to change(...).by(...)

 

 
 include  用于对数组/hash/字符串进行验证 
 raise_error  可以加参数表示具体抛出的错误
 be_within + of  
be_within(Y).of(X)

 x+-Y范围内

   
mock
   
   
   
   
   
feature
   
   
   
   
   
posted @ 2019-10-09 11:00  懒虫哥哥  阅读(360)  评论(0编辑  收藏  举报