Тестирование проблемы/модуля, использующего 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
, что достаточно для теста. Но я продолжаю читать, что вы должны тестировать модули изолированно.
Ищете какое-то руководство/стратегию относительно правильного подхода.
Ответы
Ответ 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