【问题标题】:How to test uniqueness of Coupon/Promo-Codes?如何测试优惠券/促销代码的唯一性?
【发布时间】:2012-08-10 11:59:10
【问题描述】:

我有一个模型PromoCode,它有一个.generate! 方法,它调用.generate,它使用SecureRandom.hex(5) 生成一个字符串并将其保存到数据库中:

class PromoCode < ActiveRecord::Base
  class << self
    def generate 
      SecureRandom.hex 5
    end

    def generate!
      return create! code: generate
    end
  end
end

现在我想编写一个规范来测试生成的字符串的唯一性。只要生成了不存在的 PromoCode,就应该调用 .generate 方法。

我不知道该怎么做,因为我不能真正删除 .generate 方法来返回固定值(因为那样它会陷入无限循环)。

这是迄今为止模型的通过规范:

describe PromoCode do
  describe ".generate" do
    it "should return a string with a length of 10" do
      code = PromoCode.generate
      code.should be_a String
      code.length.should eql 10
    end
  end

  describe ".generate!" do
    it "generates and returns a promocode" do
      expect {
        @promo = PromoCode.generate!
      }.to change { PromoCode.count }.from(0).to(1)

      @promo.code.should_not be_nil
      @promo.code.length.should eql 10
    end

    it "generates a uniq promocode" do
    end
  end
end

任何方向表示赞赏。

【问题讨论】:

    标签: ruby-on-rails rspec rspec2 rspec-rails


    【解决方案1】:

    Rspec 的and_return 方法允许您指定将循环通过的多个返回值

    例如你可以写

    PromoCode.stub(:generate).and_return('badcode1', 'badcode2', 'goodcode')
    

    这将导致第一个调用 generate 返回“badcode1”,第二个“badcode2”等...然后您可以检查返回的促销代码是否使用正确的代码创建。

    如果您想成为竞争条件证明,您将需要一个数据库唯一性约束,因此您的代码实际上可能想要成为

    def generate!
       create!(...)
    rescue ActiveRecord::RecordNotUnique
      retry
    end
    

    在您的规范中,您将存根创建!第一次引发第二次返回值的方法

    【讨论】:

    • 哇,不知道。而且实现比我想象的还要优雅(使用循环和 find_by_code)。非常感谢!
    【解决方案2】:

    还有类似的情况:创建一个PromoCode,保存结果,然后尝试使用之前code 对象的PromoCode 创建一个新的PromoCode

    it "should reject duplicate promocode" do
      @promo = PromoCode.generate!
      duplicate_promo = PromoCode.new(:code => @promo.code)
      duplicate_promo.should_not be_valid
    end
    

    另外,这是模型级别的,我假设您在数据库中有一个密钥,可以将您从竞争条件中解救出来......

    【讨论】:

    • 我需要确保generate! 生成代码 - 无论如何,直到生成唯一代码。
    • 从我的角度来看,这将是另一个测试。在这里,您只是测试它是否拒绝重复的促销代码。
    • 好的。但那是我一开始就想写的测试;)
    【解决方案3】:

    如果您将促销代码保存在数据库中,您将在模型中为 uniq 促销代码添加验证。所以你也可以在 rspec 中测试。

    这样,

    it { should validate_uniqueness_of(:promocode) }
    

    【讨论】:

      【解决方案4】:

      此答案基于您的评论:

      我需要确保生成!生成代码 - 无论如何, 直到生成唯一代码。

      我觉得你可能很难正确测试这个。单元测试“无论如何”具有无限循环的情况可能有点棘手。

      我不知道该怎么做,因为我不能真正地存根 .generate 方法来返回固定值(因为那样它会卡在一个无限循环中)。

      要考虑的一种可能性是,您是否尝试了两种方法而不是其中一种? (也就是说,找到一种方法让它在某些情况下返回一个固定的数字,并最终触发实际的随机数。实例变量计数器可能会有所帮助;将其设置为随机数,将其倒数,当它大于零时返回固定数字,或类似的东西)。不过,这仍然不是一个完美的测试,甚至不是一个非常好的测试。

      可能值得更多地研究生成相似字符串的方法,这些字符串很有可能是唯一的,并有一些数学证明证明它是这样的。我也不是说这也是最实用的想法,但如果你真的需要证明(就像你试图用测试做的那样),它可能是更可能的解决方案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-04-02
        • 2013-04-22
        • 1970-01-01
        • 2011-06-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-11
        • 2014-11-26
        相关资源
        最近更新 更多