ChatGPT解决这个技术问题 Extra ChatGPT

如何在 Rails 中测试关注点

鉴于我在具有 full_name 方法的 Rails 4 应用程序中有一个 Personable 问题,我将如何使用 RSpec 进行测试?

关注/personable.rb

module Personable
  extend ActiveSupport::Concern

  def full_name
    "#{first_name} #{last_name}"
  end
end
你用的是什么测试框架?还要记住 Personable 只是一个普通的 Ruby 模块。像测试任何其他 mixin 一样测试它。
ActiveSupport::Concern 没有从 Rails 中删除吗?我以为它在不久前就走了。
@LeeJarvis 我正在与 FactoryGirl 一起使用 Rspec
@Russell 我同意。也就是说,我不会仅仅因为他们遵循 Rails-y 的方式做一些我不同意的事情就帮助他们解决问题。无论如何,这有点逃避这个问题的主题:-)

R
Rimian

您找到的方法当然可以用来测试一些功能,但看起来很脆弱——您的虚拟类(实际上只是您解决方案中的一个 Struct)可能表现得像一个真正的类,也可能不像您关心的一个真正的类。include此外,如果您尝试测试模型问题,您将无法执行测试对象的有效性或调用 ActiveRecord 回调之类的操作,除非您相应地设置数据库(因为您的虚拟类没有数据库表支持它)。此外,您不仅要测试关注点,还要测试关注点在模型规范中的行为。

那么为什么不一石两鸟呢?通过使用 RSpec 的 shared example groups,您可以针对使用它们的实际类(例如模型)测试您的关注点并且您将能够在使用它们的任何地方测试它们。而且您只需编写一次测试,然后将它们包含在任何使用您关注的模型规范中。在您的情况下,这可能看起来像这样:

# app/models/concerns/personable.rb
module Personable
  extend ActiveSupport::Concern

  def full_name
    "#{first_name} #{last_name}"
  end
end

# spec/concerns/personable_spec.rb
require 'spec_helper'

shared_examples_for "personable" do
  let(:model) { described_class } # the class that includes the concern

  it "has a full name" do
    person = FactoryBot.build(model.to_s.underscore.to_sym, first_name: "Stewart", last_name: "Home")
    expect(person.full_name).to eq("Stewart Home")
  end
end

# spec/models/master_spec.rb
require 'spec_helper'
require Rails.root.join "spec/concerns/personable_spec.rb"

describe Master do
  it_behaves_like "personable"
end

# spec/models/apprentice_spec.rb
require 'spec_helper'

describe Apprentice do
  it_behaves_like "personable"
end

当你开始做你关心的事情时,这种方法的优势变得更加明显,比如调用 AR 回调,在这种情况下,除了 AR 对象之外的任何事情都不会做。


这样做的一个缺点是它会减慢 parallel_tests。我认为最好有单独的测试而不是使用 shared_examples_forit_behaves_like
@ArtemKalinchuk 我不确定这是不是真的,每个 github.com/grosser/parallel_tests/issues/168 parallel_tests 基于每个文件,因此共享示例不应减慢速度。我还认为,正确分组共享行为胜过测试速度。
确保在您的 spec_helper.rb github.com/rspec/rspec-core/issues/407#issuecomment-1409871 中包含 concerns 目录
我找不到任何关于在该链接中包含关注目录的信息。你能澄清一下这是怎么做到的吗?我无法让我的 RSpec 测试识别出我关心的一个模块。
不要将 _spec 添加到包含 shared_examples_for(在本例中为 personable_spec.rb)的文件名中,否则您将收到误导性警告消息 - github.com/rspec/rspec-core/issues/828
K
Kyle Decot

为了回应我收到的评论,这就是我最终做的事情(如果有人有改进,请随时发布):

规范/关注/personable_spec.rb

require 'spec_helper'

describe Personable do
  let(:test_class) { Struct.new(:first_name, :last_name) { include Personable } }
  let(:personable) { test_class.new("Stewart", "Home") }

  it "has a full_name" do
    expect(personable.full_name).to eq("#{personable.first_name} #{personable.last_name}")
  end
end

是的,如果它们碰巧测试一个名为 Person 的真实类,这将破坏其他测试。我会编辑修复。
这行不通。它给了我错误:undefined method 'full_name' for #<struct first_name="Stewart", last_name="Home">
尝试包括 Personable 而不是扩展它。我会更新答案。
现在效果很好。感谢您为我指明正确的方向并帮助我重构@Russell
效果很好,看起来不错
l
lobati

另一个想法是使用 with_model gem 来测试这样的事情。我希望自己测试一个问题,并看到了 pg_search gem doing this。这似乎比在单个模型上进行测试要好得多,因为这些可能会发生变化,并且在规范中定义您将需要的东西很好。


N
Nathan Willson

以下对我有用。就我而言,我担心的是调用生成的 *_path 方法,而其他方法似乎不起作用。这种方法将使您能够访问一些仅在控制器上下文中可用的方法。

关心:

module MyConcern
  extend ActiveSupport::Concern

  def foo
    ...
  end
end

规格:

require 'rails_helper'

class MyConcernFakeController < ApplicationController
  include MyConcernFakeController
end

RSpec.describe MyConcernFakeController, type: :controller do    
  context 'foo' do
    it '' do
      expect(subject.foo).to eq(...)
    end
  end
end

如果在 controllers/concerns/my_concern.rb 中定义了关注点,您的关注点规范在哪里? spec/controllers/concerns/my_concern_spec.rb 还是 spec/controllers/my_concern_fake_controller_spec.rb
我认为这是一个偏好问题,但就我而言,我想测试关注点,而不是控制器(但是是的,它们已连接)所以您的第一个建议:spec/controllers/concerns/my_concern_spec.rb
R
Rimian

只需在规范中包含您的关注并测试它是否返回正确的值。

RSpec.describe Personable do
  include Personable

  context 'test' do
    let!(:person) { create(:person) }

    it 'should match' do
       expect(person.full_name).to eql 'David King'
    end
  end
end