我有一个模型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
任何指示赞赏。
答案 0 :(得分:3)
如下所示:创建PromoCode
,保存结果,然后尝试使用上一个PromoCode
对象的code
创建新的PromoCode
:< / p>
it "should reject duplicate promocode" do
@promo = PromoCode.generate!
duplicate_promo = PromoCode.new(:code => @promo.code)
duplicate_promo.should_not be_valid
end
此外,这是模型级别,我假设您在数据库中有一个密钥,可以帮助您避免竞争条件......
答案 1 :(得分:3)
Rspec&#39; and_return
方法允许您指定将循环的多个返回值
例如你可以写
PromoCode.stub(:generate).and_return('badcode1', 'badcode2', 'goodcode')
这将导致第一次调用生成以返回&#39; badcode1&#39;,第二个&#39; badcode2&#39;等等......然后,您可以检查返回的promocode是否使用正确的代码创建。
如果您想成为竞争条件证明,您将需要数据库唯一性约束,因此您的代码可能实际上想要
def generate!
create!(...)
rescue ActiveRecord::RecordNotUnique
retry
end
在您的规范中,您将存根创建!提高第一次但第二次返回值的方法
答案 2 :(得分:0)
如果将promocode保存在数据库中,您可以在模型中为uniq promocode添加验证。所以你也可以在rspec中测试它。
像这样,
it { should validate_uniqueness_of(:promocode) }
答案 3 :(得分:0)
此答案基于您的评论:
我需要确保生成!生成代码 - 无论如何, 直到生成了唯一的代码。
我觉得你可能很难正确地测试它。无限循环的单元测试“无论什么”情况都可能是一个棘手的主题。
我不知道如何做到这一点,因为我无法真正存根.generate方法返回固定值(因为它会被卡在无限循环中)。
考虑的一种可能性可能是,如果不是做其中一个,你试过两个? (也就是说,找到一种方法使它在某些情况下返回一个固定的数字,并最终触发实际的随机数。一个实例变量计数器可能有帮助;将它设置为一个随机数,将其计数下来,当它大于零时返回固定数字,或沿着这些线的东西)。然而,这仍然不是一个完美的测试,或者甚至是一个非常好的测试。
可能值得深入研究生成类似字符串的方法,这些字符串很有可能是唯一的,并且有一些数学证明就是这样。我不是说这也是最实际的想法,但如果你真的需要证明(正如你试图用测试那样),那么它可能是更可能的解决方案。