除非另一个字段为真 rails,否则如何测试验证存在?

how to test validate presence unless another field is true rails?

鉴于此代码:

field :start_now, type: Boolean, :default => true
field :time_zone
validates :time_zone, inclusion: { 
              in: ActiveSupport::TimeZone.zones_map.keys 
           }, unless: :start_now?

我创建了这个 rspec 测试,但它很长而且不干。像这样的唯一原因是 "unless" 条件:

describe "#time_zone" do
  context "scheduled" do
    before :each do
       subject.start_now = false
    end
    it { is_expected.to validate_inclusion_of(:time_zone).in_array(ActiveSupport::TimeZone.zones_map.keys) }
  end
  context "run now" do
    before :each do
       subject.start_now = true
    end
    it { is_expected.not_to validate_inclusion_of(:time_zone).in_array(ActiveSupport::TimeZone.zones_map.keys) }
  end
end

有更短的方法吗?

真的没有必要让它比这更短。这两个测试都准确地描述了他们正在测试的内容,而且它们非常具体 关于那些测试。

根据我在测试中看到的情况,您希望您的测试是 DAMP。这并不意味着它们不干燥,但它确实意味着你不应该向后弯曲以使其尽可能小和窄。

您的代码有两个不同且互斥的状态,这取决于布尔值 start_now?。你必须测试这两种状态,而且真的没有办法解决这个问题。也没有重复;您正在测试两个不同的分支。

虽然您的上下文 could use a wee bit of a verbiage clean up,但它们非常善于描述您需要做什么。

这里和改进。下一个代码更干燥,同时像以前一样更潮湿:

describe "#time_zone" do    
  let(:validate_time_zone) { validate_inclusion_of(:time_zone).in_array(ActiveSupport::TimeZone.zones_map.keys }

  context "scheduled" do
    before :each do
       subject.start_now = false
    end
    it { is_expected.to validate_time_zone }
  end
  context "run now" do
    before :each do
       subject.start_now = true
    end
    it { is_expected.not_to validate_time_zone}
  end
end

好的,所以我明白 DAMP(对我来说是一个新词)很好。我今天感觉好多了,因为我一直在思考测试太干而使它们很难阅读的问题。

有时我喜欢使用在外部 before :each 中调用的每个上下文中的 let 来编写规范。这避免了为每个上下文重复 before :each

describe "#time_zone" do    
  let(:validate_time_zone) do
    validate_inclusion_of(:time_zone).in_array(ActiveSupport::TimeZone.zones_map.keys
  end

  before :each do
    subject.start_now = start_now
  end

  context "scheduled" do
    let(:start_now) { false }
    it { is_expected.to validate_time_zone }
  end

  context "run now" do
    let(:start_now) { true }
    it { is_expected.not_to validate_time_zone}
  end
end