【问题标题】:Checking The WHERE CLAUSE clause in rspec testing检查 rspec 测试中的 WHERE CLAUSE 子句
【发布时间】:2020-04-15 06:42:55
【问题描述】:

我在 rails 中有一个命名范围,并且我有一个按名称命名的模型产品

class Product < ApplicationRecord
 scope :old_products, -> { where("tagged_with = ?","old") }
end

是否有任何机构遇到过检查主题的过程,该主题在活动记录中使用 where 并且可以检查命名范围实际包含的 where 子句

在 rspec spec/models/product_spec.rb 中

describe Product do
  describe "checking scope clauses" do
  subject { Product.old_products }
    its(:where_clauses)   { should eq([
      "tagged_with = 'old'"
    ]) }
  end
  end
end

顺便说一句,我使用 rspec-2.89 版本和 rails-5 版本,所以我们有机会检查和验证 where 子句

【问题讨论】:

  • 除非您正在开发必须真正测试生成的 SQL 的代码,否则不应测试为您生成的 RAW SQL Active Record。如果您遵循测试行为,而不是实施策略,这对您来说会很容易。

标签: ruby-on-rails rspec


【解决方案1】:
describe Product do
  describe "checking scope clauses" do
  subject { Product.old_products }
    expect(subject.values[:where].instance_values['predicates'].to eq(["tagged_with = 'old'"])
  end
end

【讨论】:

    【解决方案2】:
    # This is better, more readable syntax for scope declaration
    class Product < ApplicationRecord
      scope :old_products, -> { where(tagged_with: 'old') }
    end
    
    # Something like this would work
    describe Product do
      context 'scopes' do
        # Set up something that will always be excluded from the scopes
        let!(:product)     { create :product }
        let!(:scoped_list) { create :product, 3, tagged_with: tag }
    
        shared_examples_for 'returns scoped records' do
          # This should work with shoulda-matchers 
          # (https://github.com/thoughtbot/shoulda-matchers)
          # Could also not use subject and do something like:
          #   expect(
          #     described_class.send(scope_name.to_sym) 
          #   ).to contain_exactly(scoped_list)
          # and declare let(:scope_name) in your describe blocks
          it 'returns scoped products' do
            should contain_exactly(scoped_list)
          end
        end
    
        describe '.old_products' do
          subject(:old_products) { described_class.old_products }
    
          let(:tag) { 'old' }
    
          it_behaves_like 'returns scoped records'
        end
    
        describe '.other_scope' do
          subject(:other_scope) { described_class.other_scope }
    
          let(:tag) { 'other_tag' }
    
          it_behaves_like 'returns scoped records'
        end
      end
    end
    
    • 测试实际的 SQL 没有任何价值——它是由 Rails 生成的;您要测试的是您的范围是否返回正确的对象
    • 在声明范围测试块时,我会使用 context 而不是 describe,因为您没有描述类或实例方法
    • 使用单引号而不是双引号,除非您正在执行字符串插值 - 它更高效
    • 如果您处于项目的早期并且每个产品只有一个标签,我还将tagged_with 列重命名为tag

    【讨论】:

      【解决方案3】:

      我个人认为检查返回的范围 SQL 是不够的。我测试old_products 的方式是:

      describe Product do
        describe "scopes" do
          describe "old_products" do
            let!(:old_product) {Product.create(tagged_with: 'old')}
            let!(:not_old_product) {Product.create(tagged_with: 'sth_else')}
            subject { Product.old_products }
      
            it "returns the product(s) with tagged_with = old" do
              expect(subject).to eq([old_product])
            end
          end
        end
      end
      

      如果你还想检查返回查询,不妨试试:

      it "..." do
        expect(subject.to_sql).to eq("SELECT \"products\".* FROM \"products\" WHERE \"products\".\"tagged_with\" = 'old'")
      end
      

      【讨论】:

      • 是的,但是你是怎么做上述过程的
      • 是的,不要测试生成的 sql,因为您无法控制它(您不希望检查/测试实际查询的格式)。检查代码的意图是否与结果匹配。当然,这会慢一些(检查生成的查询与实际运行查询并比较找到的结果),但也不那么脆弱。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多