为什么针对destroy_all的单元测试失败?

yre*_*uta 1 rspec ruby-on-rails ruby-on-rails-4

所以我有这个型号代码:

def self.cleanup
    Transaction.where("created_at < ?", 30.days.ago).destroy_all
  end
Run Code Online (Sandbox Code Playgroud)

而这个rspec单元测试:

describe 'self.cleanup' do
    before(:each) do
      @transaction = Transaction.create(seller:item.user, buyer:user, item:item, created_at:6.weeks.ago)
    end

    it 'destroys all transactions more than 30 days' do
      Transaction.cleanup
      expect(@transaction).not_to exist_in_database
    end
  end
Run Code Online (Sandbox Code Playgroud)

与这些工厂:

FactoryGirl.define do
  factory :transaction do
    association :seller, factory: :user, username: 'IAMSeller'
    association :buyer, factory: :user, username: 'IAmBuyer'
    association :item
  end

  factory :old_transaction, parent: :transaction do
    created_at 6.weeks.ago
  end
end
Run Code Online (Sandbox Code Playgroud)

使用此rspec自定义匹配器:

RSpec::Matchers.define :exist_in_database do
  match do |actual|
    actual.class.exists?(actual.id)
  end
end
Run Code Online (Sandbox Code Playgroud)

当我将规格更改为:

describe 'self.cleanup' do
    let(:old_transaction){FactoryGirl.create(:old_transaction)}

    it 'destroys all transactions more than 30 days' do
      Transaction.cleanup
      expect(old_transaction).not_to exist_in_database
    end
  end
Run Code Online (Sandbox Code Playgroud)

测试失败了.我还尝试手动创建一个事务并将其分配给:old_transaction with let()但这也使测试失败.

为什么它只在我在before(:each)块中使用实例变量时才通过?

提前致谢!

编辑:失败的输出

1) Transaction self.cleanup destroys all transactions more than 30 days
     Failure/Error: expect(old_transaction).not_to exist_in_database
       expected #<Transaction id: 2, seller_id: 3, buyer_id: 4, item_id: 2, transaction_date: nil, created_at: "2014-02-26 10:06:30", updated_at: "2014-04-09 10:06:32", buyer_confirmed: false, seller_confirmed: false, cancelled: false> not to exist in database
     # ./spec/models/transaction_spec.rb:40:in `block (3 levels) in <top (required)>'
Run Code Online (Sandbox Code Playgroud)

Aar*_*n K 5

let很懒.所以在你失败的规范中,这是事件的顺序:

  1. Transaction.cleanup
  2. old_transaction = FactoryGirl.create(:old_transaction)
  3. expect(old_transaction).not_to exist_in_database

因此,您尝试清理之后创建事务.

有多种选择:

不要let用于此

除非你有其他规格想要告诉其他开发者:

我完全打算让所有这些规范引用完全相同的对象

我个人觉得,你最好在内联交易.

it do
  transaction = FactoryGirl.create(:old_transaction)

  Transaction.cleanup

  expect(transaction).not_to exist_in_database
end
Run Code Online (Sandbox Code Playgroud)

使用change匹配器

这是我个人的选择,因为它清楚地表明了预期的行为:

it do
  expect{
    Transaction.cleanup
  }.to change{ Transaction.exists?(old_transaction.id) }.to false
end
Run Code Online (Sandbox Code Playgroud)

这适用letchange块在块之前和之后运行expect.所以在第一次传递时,old_transaction实例化它id可以被检查.

在清理之前使用before或参考old_transaction

IMO这看起来很奇怪:

before do
  old_transaction
end

it do
  old_transaction # if you don't use the before
  Transaction.clean
  # ...
end
Run Code Online (Sandbox Code Playgroud)

使用 let!

let!不是懒惰的.从本质上讲,它是做正常的别名let,然后在a中调用它before.我不是这种方法的粉丝(请参阅The bang是为了解释细节的原因).