Ruby on rails 使用RSpec测试URL(regex)验证

Ruby on rails 使用RSpec测试URL(regex)验证,ruby-on-rails,ruby,validation,testing,rspec,Ruby On Rails,Ruby,Validation,Testing,Rspec,Rails 4.2应用程序有几个模型,其属性包含URL。URL验证通过验证:网站URL,格式:{with://\A(https?| ftp):\/\/(-)([^\s\/?\.-]+\.+(\/[^\s]*)?\z/i} 我需要用RSpec3.5测试URL验证。确保一些众所周知的XSS模式不通过验证,并且最常用的URL模式通过验证,这一点很重要 理想情况下,我希望避免为正在测试的每个有效和无效URL添加一个测试,这样就不会污染rspec-fd输出。然而,这可能需要创建两个测试(一个用于有效URL

Rails 4.2应用程序有几个模型,其属性包含URL。URL验证通过
验证:网站URL,格式:{with://\A(https?| ftp):\/\/(-)([^\s\/?\.-]+\.+(\/[^\s]*)?\z/i}

我需要用RSpec3.5测试URL验证。确保一些众所周知的XSS模式不通过验证,并且最常用的URL模式通过验证,这一点很重要

理想情况下,我希望避免为正在测试的每个有效和无效URL添加一个测试,这样就不会污染
rspec-fd
输出。然而,这可能需要创建两个测试(一个用于有效URL,另一个用于无效URL),并为每个测试添加多个期望值(每个URL一个期望值),这似乎不是一个好主意

到目前为止,我提出的最佳解决方案是以下共享示例您能想出一种更好的方法来彻底测试URL验证吗?

RSpec.shared_examples "url validation" do |attribute|
  INVALID_URLS = [
      "invalidurl",
      "inval.lid/urlexample",
      "javascript:dangerousJs()//http://www.validurl.com",
      # Literal array is required for \n to be parsed
      "http://www.validurl.com\n<script>dangerousJs();</script>"
  ]

  VALID_URLS = [
      "http://validurl.com",
      "https://validurl.com/blah_blah"
  ]

  context "with invalid URLs in #{attribute}" do
    INVALID_URLS.each do |url|
      it "is invalid with #{url}" do
        object = FactoryGirl.build(factory_name(subject), attribute => url)
        object.valid?
        expect(object.errors[attribute]).to include("is invalid")
      end
    end
  end

  context "with valid URLs in #{attribute}" do
    VALID_URLS.each do |url|
      it "is valid with #{url}" do
        object = FactoryGirl.build(factory_name(subject), attribute => url)
        expect(object).to be_valid
      end
    end
  end

编辑:为有效和无效的URL添加了
上下文
,因此
rspec-fd
输出组织得更好,即使以随机顺序执行了大量URL验证测试。

您的解决方案也不错。我有理由继续用更多的
共享示例对其进行分解:

RSpec.shared_examples "an invalid URL for attribute" do |url, attribute|
  let(:object) { FactoryGirl.build(factory_name(subject), attribute => url) }
  before(:each) { object.valid? }
  specify { expect(object.errors[attribute]).to include("is invalid") }
end

RSpec.shared_examples "a valid URL for attribute" do |url, attribute|
  let(:object) { FactoryGirl.build(factory_name(subject), attribute => url) }
  specify { expect(object).to be_valid }
end

RSpec.shared_examples "URL validation" do |attribute|
  [ "invalidurl",
    "inval.lid/urlexample",
    # ...
  ].each do |url|
    it_behaves_like "a valid URL for attribute", url, attribute
  end

  [ "http://validurl.com",
    "https://validurl.com/blah_blah"
  ].each do |url|
    it_behaves_like "an invalid URL for attribute", url, attribute
  end
end

重复的
let(:object)
让我很烦,但我没有立即的解决办法。

谢谢你的回答。我也尝试过,但无法避免重复对象创建。即使在相同的
shared\u示例
块中,将URL从循环中传递到
let
块也很麻烦。@Brunofaca只是想澄清一下,上述解决方案是否适用于您的用例?如果您有任何问题,请告诉我。这很有效。但是,您能否解释一下以这种方式分解它的好处(创建3个共享示例)?在我看来,尽管这种方法很聪明/复杂,但它在不减少重复或重复的情况下引入了额外的复杂性。根据你的经验,我可能遗漏了一些东西。@Brunofaca你说得对,这里没有太大的区别。我喜欢这种方法,因为参数化的示例组更具声明性。这些测试似乎是一个更大的套件的一部分,它的设计方式让你的手有点紧张(因此
object
而不是
subject
)。您可能想在上发布更大的代码部分,看看他们是否有任何提示。谢谢您的帮助。我会的。
RSpec.shared_examples "an invalid URL for attribute" do |url, attribute|
  let(:object) { FactoryGirl.build(factory_name(subject), attribute => url) }
  before(:each) { object.valid? }
  specify { expect(object.errors[attribute]).to include("is invalid") }
end

RSpec.shared_examples "a valid URL for attribute" do |url, attribute|
  let(:object) { FactoryGirl.build(factory_name(subject), attribute => url) }
  specify { expect(object).to be_valid }
end

RSpec.shared_examples "URL validation" do |attribute|
  [ "invalidurl",
    "inval.lid/urlexample",
    # ...
  ].each do |url|
    it_behaves_like "a valid URL for attribute", url, attribute
  end

  [ "http://validurl.com",
    "https://validurl.com/blah_blah"
  ].each do |url|
    it_behaves_like "an invalid URL for attribute", url, attribute
  end
end