Подтвердить что ты не робот

Тестирование проблемы/модуля, использующего ActiveRecord

СЦЕНАРИЙ Я выделил проблему Taggable. Это модуль, который позволяет любой модели поддерживать тегирование. Я включил эту проблему/модуль в такие модели, как User, Location, Places, 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, что достаточно для теста. Но я продолжаю читать, что вы должны тестировать модули изолированно.

Ищете какое-то руководство/стратегию относительно правильного подхода.

4b9b3361

Ответ 1

Я бы предположил, что DummyClass является родовым ActiveRecord::Base ребенком с очень маленьким пользовательским кодом, кроме как только 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 и их базовых таблиц базы данных.

Темп позволяет создавать произвольные модели ActiveRecord, поддерживаемые временной таблицей SQL для использования в тестах. Возможно, вам понадобится сделать что-то вроде этого, если вы тестируете модуль, который должен быть смешан с моделями ActiveReord без передачи на конкретный класс.

Ответ 2

Я пошел использовать ActiveRecord Tableless вместо Temping, который, кажется, немного устарел на данный момент.

Я установил свой тест точно так же, как 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, поэтому он все равно может не помочь с тем, что вы хотели для тестирования.

Ответ 3

Вот мое решение аналогичной проблемы:

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 mocks) были извлечены в rspec-activemodel-mocks в rspec 3.0.
Также вам нужно использовать shoulda-matchers для сопоставлений ассоциаций.

Ответ 4

Как указано в @StuartM 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