考虑以下模型:
class Model < ActiveRecord::Base
scope :a, -> () { where(a: 1) }
scope :b, -> () { where(b: 1) }
scope :a_or_b, -> () { a.or(b) }
end
现在为了正确测试每个范围,我至少会提供一个示例 匹配,一个不匹配每个可能的变量。像这样:
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
但后来感觉我正在重新测试.a
并在.a_or_b
测试中.b
,并且 如果我再用另一个范围来创作它,它会越来越大。
处理这个问题的理智方法是什么?
另外:这是单元测试还是集成测试?
这是一个艰难的问题。我想说你必须在全覆盖和你的规格可读性之间找到一个亚里士多德的意思。测试影响作用域行为的可能状态的每个组合可能不合理。
此外,它也不是真正的单元测试,因为它与数据库层耦合。
我的方法是这样的:
不要测试简单的范围(如示例中的a
和b
(,相信AR经过良好测试,明确范围名称并保留它。
在更复杂的作用域中,可以反转组合测试的方式: 创建一次标本,然后定义对不同范围的期望(并使标本名称非常清晰,就像您已经做的那样(。
这将稍微减小规范的大小,并且更容易阅读此类规范。但是,如果您想完全覆盖每个范围,它仍然会增加规格大小。
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
为了使这个^更具可读性,我建议创建一个自定义匹配器,你可以这样使用:
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