简体   繁体   English

如何测试复合 Rails 模型范围?

[英]How to test composite Rails model scopes?

Consider the following model:考虑以下模型:

class Model < ActiveRecord::Base
  scope :a,       -> () { where(a: 1) }
  scope :b,       -> () { where(b: 1) }
  scope :a_or_b,  -> () { a.or(b) }
end

now to properly test each scope, I would at least provide an exaple that matches and one that doesn''t for each possible variable.现在为了正确测试每个范围,我至少会提供一个匹配的示例,而每个可能的变量都不匹配。 Something like this:像这样的东西:

RSpec.describe Model do
  describe "scopes" do
    describe ".a" do
      subject { Model.a }
      let!(:with_a_0) { create(:model, a: 0) }
      let!(:with_a_1) { create(:model, a: 1) }

      it { should_not include(with_a_0) }
      it { should     include(with_a_1) }
    end

    describe ".b" do
      subject { Model.b }
      let!(:with_b_0) { create(:model, b: 0) }
      let!(:with_b_1) { create(:model, b: 1) }

      it { should_not include(with_b_0) }
      it { should     include(with_b_1) }
    end

    describe ".a_or_b" do
      subject { Model.a_or_b }
      let!(:with_a_0_and_b_0) { create(:model, a: 0, b: 0) }
      let!(:with_a_0_and_b_1) { create(:model, a: 0, b: 1) }
      let!(:with_a_1_and_b_0) { create(:model, a: 1, b: 0) }
      let!(:with_a_1_and_b_1) { create(:model, a: 1, b: 1) }

      it { should_not include(with_a_0_and_b_0) }
      it { should     include(with_a_0_and_b_1) }
      it { should     include(with_a_1_and_b_0) }
      it { should     include(with_a_1_and_b_1) }
    end
  end
end

But then it feels like I'm retesting .a and .b on the .a_or_b test, and if I compose it again, with yet another scope, it'll get bigger and bigger.但是感觉就像我在.a_or_b测试中重新测试.a.b ,如果我再次.a_or_b它,使用另一个范围,它会变得越来越大。

What is the sane way of dealing with this?处理这个问题的明智方法是什么?

Also: is this a unit or integration test?另外:这是单元测试还是集成测试?

That's a tough one.这是一个艰难的。 I'd say you have to find an Aristotelian mean between full coverage, and your specs readability.我会说你必须在全面覆盖和你的规范可读性之间找到亚里士多德的平均值。 It's probably not reasonable to test every combination of possible states that influence how your scopes are behaving.测试影响示波器行为方式的所有可能状态的组合可能是不合理的。

Also it's not really a unit test, because it's coupled with the DB layer.它也不是真正的单元测试,因为它与 DB 层耦合。

My approach is this:我的方法是这样的:

Don't test simple scopes (like a and b in your example), trust that AR is well tested, make yous scope names clear and leave it.不要测试简单的范围(例如您的示例中的ab ),相信 AR 已经过良好测试,让您的范围名称清晰并保留它。

In more complex scopes, you can invert the way you compose tests: Create specimens once, then define expectations for different scopes (and make the specimens names very clear, like you're doing already).在更复杂的范围内,您可以颠倒编写测试的方式:创建一次样本,然后定义对不同范围的期望(并使样本名称非常清晰,就像您已经在做的那样)。

This will reduce the size of the spec a bit, and it's easier to read such specs.这将稍微减少规范的大小,并且更容易阅读此类规范。 But it still will grow the spec size if you want to have full coverage for every scope.但是,如果您想对每个示波器进行全面覆盖,它仍然会增加规格尺寸。

RSpec.describe Model do
  describe "scopes" do
    let!(:with_a_0_and_b_0) { create(:model, a: 0, b: 0) }
    let!(:with_a_0_and_b_1) { create(:model, a: 0, b: 1) }
    let!(:with_a_1_and_b_0) { create(:model, a: 1, b: 0) }
    let!(:with_a_1_and_b_1) { create(:model, a: 1, b: 1) }

    it { expect(described_class.a).to include(with_a_1_and_b_1).and include(with_a_1_and_b_1) }
    it { expect(described_class.a).not_to include(with_a_0_and_b_0) }

    # you get the idea

To make this^ even more readable, I'd suggest creating a custom matcher , you could use like this:为了使这个^ 更具可读性,我建议创建一个自定义 matcher ,您可以像这样使用:

  it do 
    expect(described_class.a)
      .to retrieve(with_a_1_and_b_0, with_a_1_and_b_1)
      .and not_retrieve(with_a_0_and_b_0, with_a_0_and_b_1)
  end

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM