【问题标题】:how to test validate presence unless another field is true rails?除非另一个字段是真正的轨道,否则如何测试验证存在?
【发布时间】:2025-12-04 15:55:01
【问题描述】:

鉴于此代码:

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

我已经创建了这个 rspec 测试,但它很长而且不是 DRY。而这样的唯一原因是因为“除非”条件:

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

有没有更短的方法可以做到这一点?

【问题讨论】:

    标签: ruby-on-rails ruby validation rspec dry


    【解决方案1】:

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

    根据我对测试的了解,您希望您的测试是DAMP。这并不意味着它们不干燥,但它确实意味着你不应该向后弯腰以使其尽可能小和窄。

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

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

    【讨论】:

    • 谢谢,这是我经常与一些同事讨论的问题。现在我可以把这个链接发给他们了:D
    【解决方案2】:

    这里和改进。下一个代码更 DRY,同时更 DAMP:

    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(对我来说是一个新词)很好。我今天感觉好多了,因为我一直在思考测试过于枯燥和难以阅读的问题。

    【讨论】:

      【解决方案3】:

      有时我喜欢在外部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
      

      【讨论】:

        最近更新 更多