2017-09-21 19 views
0

我想测试一个使用ActiveRecord适量的方法。我一直听说单元测试方法,而不是使用数据库是最好的方法。然而,这对我来说的缺点是如果你错误地存活了ActiveRecord的响应呢?我可能会错误地将任何ActiveRecord在现实中返回的内容剔除,现在我的测试不准确。单元测试具有两个数据库查询的方法。我应该剔除数据库调用吗? Rails/Rspec

FYI一个user有许多coupons和(当它应用于order的次)coupon可以有很多coupon_uses

这是我的方法:

def get_coupons_and_uses_for_user 
     coupons = Promotions::Coupon 
     .includes(:coupon_uses) 
     .where(user_id: user_id) 

     coupons.reduce([]) do |memo, c| 
     memo << { coupon: c, coupon_uses: c.coupon_uses.order('created_at desc') } 
     end 
    end 

上述方法(这基本上查询用户的优惠券,然后返回其使用相关联的每个优惠券)有两个ActiveRecord的要求,这两者我需要的,如果存根出我没有在我的测试中使用一个数据库:

Promotions::Coupon.includes(:coupon_uses).where(user_id: user_id)

c.coupon_uses.order('created_at desc') #The .order causes ActiveRecord to hit the database

这里我的测试:

describe "#get_coupons_and_uses_for_user" do 
    subject { service_instance.get_coupons_and_uses_for_user } 

    let(:service_instance) { described_class.new(user_id) } 
    let(:user_id) { 1 } 
    let(:coupon1) { build_stubbed(:coupon) } 
    let(:coupon2) { build_stubbed(:coupon) } 
    let(:coupons) { [coupon1, coupon2] } 
    let!(:coupon_use1) { build_stubbed(:coupon_use) } 
    let!(:coupon_use2) { build_stubbed(:coupon_use) } 
    let!(:coupon_use3) { build_stubbed(:coupon_use) } 
    let!(:coupon_use4) { build_stubbed(:coupon_use) } 

    before do 
     allow(Promotions::Coupon).to receive_message_chain(:includes, :where) { coupons } 
     allow(coupon1).to receive_message_chain(:coupon_uses, :order) { [coupon_use1, coupon_use2] } 
     allow(coupon2).to receive_message_chain(:coupon_uses, :order) { [coupon_use3, coupon_use4] } 
    end 

    it "returns the user's coupons associated with their coupon uses" do 
     expect(subject).to eq(
     [ 
      { 
      coupon: coupon1, 
      coupon_uses: [coupon_use1, coupon_use2] 
      }, 
      { 
      coupon: coupon2, 
      coupon_uses: [coupon_use3, coupon_use4] 
      } 
     ] 
    ) 
    end 
    end 

这是测试的好办法?或者我应该使用数据库?为什么或者为什么不?

这里的问题是,我碰伤了我的ActiveRecord的返回值调用是这样的:

allow(coupon1).to receive_message_chain(:coupon_uses, :order) { [coupon_use1, coupon_use2] } 

但是,如果我错了,什么该查询返回(想象一下,如果查询较多什么复杂)。这是一个有效的关注吗?

+0

可能的重复[测试方法是大量的数据库查询。单元测试可以吗?](https://stackoverflow.com/questions/46332283/testing-methods-were-lots-of-database-queries-in-them-is-it-okay-to-unit-test ) – jvillian

回答

0

不,不要将呼叫存根到数据库。

Rails约定是在模型测试中使用数据库。无论好坏,这都是Rails项目所期望的。

你不必遵循约定,但它会让你的生活更轻松。所以除非你有强制性的理由这样做,否则我会避免它。


为什么遵循这个约定?对DB运行模型试验的惯例是好几个原因:

  1. 开发者熟悉Rails可以起床快,加快该项目时,它遵循约定(在这种情况下,他们可以调试/写测试以熟悉的方式工作)

  2. 如果数据库在测试中速度较慢,则生产速度可能会很慢,这将鼓励您在开发过程中提供反馈,从而鼓励良好的数据库设计。

  3. 代码较少。将所有来自ActiveRecord模型的调用都保存到数据库中会变得麻烦,这使得重构更多的工作来在更改对ActiveRecord的调用时更新所有的方法存根。

  4. 当您升级Rails时,ActiveRecord的接口可能会更改,但存根不会。因此,在生产中对数据库运行时,您的测试可能会失败。

+0

但为什么会议在这里好?为什么它会让事情变得更简单?如果遵循极端的话......从长远来看,我只会有很多较慢的测试。 – Jwan622

+0

其他人都在关心?这些测试真的是集成测试吗? – Jwan622

+1

遵循@ csexton的建议,甚至没有升级Rails,但是例行的数据库迁移可能会导致代码崩溃。 存根调用可能会错误地让规范通过,因为他们对db的看法已经过时。 此外,根据定义ActiveRecord绑定到数据库。所以我仍然认为它是ActiveRecord上下文中的单元测试。大多数情况下,我们假设AR起作用。我们不打算写一个AR规范,它也运行原始SQL查询来检查数据是否实际在数据库中。 –