2013-05-13 67 views
69

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

顾虑/ personable.rb

module Personable 
    extend ActiveSupport::Concern 

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

你正在使用什么测试框架?还记得Personable只是一个普通的Ruby模块。像测试其他mixin一样测试它。 – 2013-05-13 15:05:30

+0

没有'ActiveSupport :: Concern'被从Rails中取出?我以为这是前一阵子。 – Russell 2013-05-13 15:06:36

+0

@LeeJarvis我沿着w/FactoryGirl使用Rspec – 2013-05-13 15:08:00

回答

118

您发现的方法肯定会测试一些功能,但看起来非常脆弱 - 您的虚拟类(实际上在您的解决方案中只是一个Struct)可能会或可能不会像您真正关注的真实类。此外,如果您试图测试模型问题,则无法执行诸如测试对象有效性或调用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 = FactoryGirl.create(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对象刚刚获得”更加明显的优势不要做。

+1

这样做的一个缺点是它会减缓'parallel_tests'。我认为单独的测试代替使用'shared_examples_for'和'it_behaves_like'会更好。 – 2014-01-21 12:23:18

+4

@ArtemKalinchuk我不确定这是否属实,根据https://github.com/grosser/parallel_tests/issues/168'parallel_tests'基于每个文件,因此共享示例不应该减慢速度。我也会争辩说,适当分组的共享行为,胜过测试速度。 – 2014-04-09 22:16:43

+8

确保在您的'spec_helper.rb'中包含'concern'目录https://github.com/rspec/rspec-core/issues/407#issuecomment-1409871 – Ziggy 2014-06-06 00:20:49

37

在回答我收到的意见,这里就是我最后做(如果任何人有改善,请随意张贴他们)

规格/顾虑/ 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 
    personable.full_name.should == "#{personable.first_name} #{personable.last_name}" 
    end 

end 
+1

是的,如果他们碰巧测试一个名为'Person'的真实类,这将会打破其他测试。我将编辑修复。 – Russell 2013-05-13 15:25:55

+0

这不起作用。它给了我错误:'未定义的方法'full_name'为#' – 2013-05-13 18:29:08

+0

尝试包括Personable而不是扩展它。我会更新答案。 – Russell 2013-05-13 21:45:53

3

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

+0

谢谢。没看过“with_model” – Subimage 2015-04-08 01:51:44

+0

是的,这太棒了。谢谢:) – 2015-05-06 17:37:36

+0

我觉得像with_model是Ryan Bates会指出的,如果他还在做railscast的话。我想念那个人。 – IAmNaN 2017-06-02 18:54:05