Я стараюсь, чтобы мои тесты были сосредоточены только на контракте для этого конкретного класса/модуля. Если я доказал поведение модуля в тестовом классе для этого модуля (обычно, включив этот модуль в тестовый класс, объявленный в спецификации для этого модуля), я не буду дублировать этот тест для производственного класса, который использует этот модуль. Но если есть дополнительное поведение, которое я хочу проверить для производственного класса или проблемы интеграции, я напишу тесты для производственного класса.
Например, у меня есть модуль под названием AttributeValidator
, который выполняет облегченные проверки типа, аналогичного ActiveRecord
. Я пишу тесты для поведения модуля в модуле спецификации:
before(:each) do
@attribute_validator = TestAttributeValidator.new
end
describe "after set callbacks" do
it "should be invoked when an attribute is set" do
def @attribute_validator.after_set_attribute_one; end
@attribute_validator.should_receive(:after_set_attribute_one).once
@attribute_validator.attribute_one = "asdf"
end
end
class TestAttributeValidator
include AttributeValidator
validating_str_accessor [:attribute_one, /\d{2,5}/]
end
Теперь в классе производства, который включает в себя модуль, я не буду повторно утверждать, что обратные вызовы сделаны, но я могу утверждать, что включен класс имеет определенный набор валидации с определенным регулярным выражением, что-то особенное для этого класса, но не воспроизводит тесты, которые я написал для модуля.В спецификации для производственного класса я хочу гарантировать, что определенные проверки будут установлены, но не то, что валидации работают в целом. Это своего рода тест интеграции, но один, который не повторяет одни и те же утверждения, которые я сделал для модуля:
describe "ProductionClass validation" do
it "should return true if the attribute is valid" do
@production_class.attribute = @valid_attribute
@production_class.is_valid?.should be_true
end
it "should return false if the attribute is invalid" do
@production_class.attribute = @invalid_attribute
@production_class.is valid?.should be_false
end
end
Существует некоторое дублирование здесь (как и большинство тестов интеграции будет иметь), но тесты доказывают два разные вещи для меня. Один набор тестов доказывает общее поведение модуля, а другой - конкретные проблемы реализации производственного класса, который использует этот модуль. Из этих тестов я знаю, что модуль будет проверять атрибуты и выполнять обратные вызовы, и я знаю, что мой производственный класс имеет определенный набор валидаций для определенных критериев, уникальных для производственного класса.
Надеюсь, что это поможет.
Когда вы говорите «функциональное покрытие для тестирования», я полагаю, что вы ссылаетесь на функциональность, которую приобретают модели, а не на тесты контроллера, которые хранятся в тестах/функциях? Спасибо за ваш ответ. Мне нравится идея тестирования модуля изолированно и написания помощника, который могут использовать другие классы, которые используют этот модуль. – tsdbrown
По функциональности я имею в виду извне. Это обычно проверка контроллера, но не всегда. В любом случае функциональное покрытие должно касаться (или, по крайней мере, пасти) всех областей системы. Если тесты вашего устройства прочны, то функционального тестирования достаточно часто, чтобы покрыть задницу. Написание слишком много тестов низкого уровня может быть плохим вложением. Если он никогда не потерпит неудачу в одиночку, то это поймает ошибки? Является ли «вероятное время отладки сохранено» * «вероятность ошибки»> «время для написания теста»? Игнорировать это, если ошибка может убить людей или ваш бизнес. –
cwninja
№ Контрольные тесты (почти) всегда плохие идеи (рассказы огурцов делают то же самое лучше), и они все равно не имеют отношения к проблеме. Просто модульный тест, как в первом примере кода. –