测试使用ActiveRecord的问题/模块

时间:2013-04-07 21:45:28

标签: ruby-on-rails ruby unit-testing minitest activesupport

SCENARIO 我提取了一个名为Taggable关注。它是一个允许任何模型支持标记的模块。我已将此问题/模块包含在UserLocationPlacesProjects等模型中。

我想为这个模块编写测试,但不知道从哪里开始。

问题
1。我可以对Taggable关注点进行隔离测试吗?
在下面的示例中,测试失败,因为测试正在寻找dummy_class table。由于has_many中的Taggable代码,我假设它正在这样做,因此它希望'DummyClass'成为ActiveRecord对象。

# /app/models/concerns/taggable.rb
module Taggable
  extend ActiveSupport::Concern

  included do
    has_many :taggings, :as => :taggable, :dependent=> :destroy
    has_many :tags, :through => :taggings
  end

  def tag(name)
    name.strip!
    tag = Tag.find_or_create_by_name(name)
    self.taggings.find_or_create_by_tag_id(tag.id)
  end
end


# /test/models/concerns/taggable_test.rb
require 'test_helpers'

class DummyClass
end

describe Taggable do
  before do
    @dummy = DummyClass.new
    @dummy.extend(Taggable)
  end

  it "gets all tags" do
    @dummy.tag("dummy tag")
    @dummy.tags.must_be_instance_of Array
  end
end

我的一部分认为,如果我只测试一个包含此模块的模型,如User那就足够了。但我一直在读,你应该单独测试模块。

寻找关于正确方法的一些指导/策略。

4 个答案:

答案 0 :(得分:5)

我建议让DummyClass成为一个普通的ActiveRecord::Base孩子,除了include Taggable之外只有很少的自定义代码,这样你就可以尽可能地隔离你的关注模块但仍然存在AR类。避免使用像User这样的“真正”类仍然会使您与这些类中的任何其他代码隔离开来,这看起来很有价值。

这样的事情:

class DummyClass < ActiveRecord::Base; end

describe Taggable do
  before do
    @dummy_class = DummyClass.new
  end
  ...
end

由于您的DummyClass可能需要与DB实际交互以测试关联等内容,因此您可能需要在测试期间在数据库中创建临时表。 temping Ruby gem可能能够提供帮助,因为它旨在创建临时ActiveRecord模型及其底层数据库表。

  

Temping 允许您创建由临时SQL表支持的任意ActiveRecord模型,以便在测试中使用。如果您正在测试一个混合到ActiveReord模型中的模块而不转发具体类,则可能需要执行此类操作。

答案 1 :(得分:2)

我选择使用ActiveRecord Tableless而不是Temping gem,这似乎有点过时了。

我将我的测试设置为Stuart Manswer完全相同,但包含了我的DummyClass中所需的has_no_table辅助方法和列。

class DummyClass < ActiveRecord::Base
  # Use ActiveRecord tableless
  has_no_table
  # Add table columns
  column :name, :string
  # Add normal ActiveRecord validations etc
  validates :name, :presence => true
end   

这适用于我需要测试的内容,这是一个使用一些其他方法扩展ActiveRecord::Base的模块,但我没有尝试使用任何has_many关联,所以它仍然可能无法帮助用你想测试的东西。

答案 2 :(得分:1)

以下是我类似问题的解决方案:

describe Taggable do
  subject { mock_model('User').send(:extend, Taggable) }

  it { should have_many(:tags) }
  ...

  describe "#tag" do
    ...
  end
end

实际上mock_model('User')可以模拟系统中任何现有的模型。

这不是一个理想的解决方案,但至少它是清楚的,并嘲笑一切。

注意mock_model(AR模拟)在rspec 3.0中被提取到rspec-activemodel-mocks
此外,您还需要使用shoulda-matchers进行关联匹配。

答案 3 :(得分:0)

正如@StuartM&#39; answer中所建议的,使用temping gem为我工作:

# test.rb/spec.rb
Temping.create :dummy_class do
  include Taggable
end

describe Taggable do
  before do
    @dummy = DummyClass.new
  end
  ...
end