15

场景 我提取了一个名为Taggable. 它是一个允许任何模型支持标记的模块。我已将此关注点/模块包含在UserLocationPlaces、等模型中Projects

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

问题
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

4 回答 4

7

我建议DummyClass你成为一个通用的ActiveRecord::Base孩子,除了 just 之外,几乎没有自定义代码include Taggable,这样你就可以尽可能地隔离你的关注模块,但仍然是一个 AR 类。避免使用您的“真实”类之一User仍然会将您与这些类中的任何其他代码隔离开来,这似乎很有价值。

所以是这样的:

class DummyClass < ActiveRecord::Base; end

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

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

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

于 2013-04-07T21:50:48.617 回答
2

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

我设置了与Stuart M在他的答案中完全相同的测试,但包括了has_no_table我的 DummyClass 中所需的辅助方法和列。

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关联,所以它仍然可能对你想要测试的内容没有帮助。

于 2015-01-28T09:42:32.260 回答
1

这是我对类似问题的解决方案:

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

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

  describe "#tag" do
    ...
  end
end

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

这不是一个理想的解决方案,但至少它很清晰并且可以模拟一切。

注意: (AR 模拟)在 rspec 3.0 中mock_model被提取到rspec-activemodel-mocks 。
您还需要将shoulda-matchers用于关联匹配器。

于 2014-10-28T11:29:39.177 回答
0

正如@StuartM's 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
于 2014-07-24T14:59:50.803 回答