Возможно, это не главное, но я думаю, что ваш код структурирован странным образом.Вместо ожидания вызова кода
categories = db.categories
categories.each do |category|
items = db.items(category)
items.each do |item|
db.subitems(item)
end
end
я бы ожидал, что он вызовет:
categories = db.categories
categories.each do |category|
items = category.items
items.each do |item|
item.subitems
end
end
У объекта категории есть элементы.Вам не нужно передавать объект категории объекту БД, чтобы получить его элементы.Либо вы не используете ActiveRecord (или DataMapper, либо ...), либо используете его странным образом.
Тогда вы можете сделать:
let(:items) {[mock('item1', :subitems => ["S1", "S2"]),
mock('item2', :subitems => ["S3", "S4"])]}
let(:categories) {[mock('category', :items => items)]}
let(:db) {double('database', :categories => categories)}
, что обходится безчтобы назвать все вещи и разделяет заглушки между всеми спецификациями.Это заглушки, а не ожидания, но поскольку это не основная функциональность, которую вы тестируете, я согласен с Аароном В. в том, что заглушки являются более подходящими.
Редактировать после ответа на комментарии и перечитываниявопрос:
Ваша основная жалоба в этом вопросе заключается в том, что
с двойным для БД, я на самом деле тестирую целую кучу вещей в одном примере
Для меня это указывало на то, что ваша проблема была с двойником, потому что это представляется причиной, по которой вы «тестируете кучу вещей».Возможно, вам не нравятся ожидания (которые могут быть способом тестирования вещей, которые очень тесно связывают тест с реализацией), цепочечный список для его работы или осознанная необходимость их повторения.Все это имеет смысл, и ваш собственный ответ, использующий цепочечные ожидания, отражает стремление к упрощению использования двойного.
Однако, при повторном прочтении, реальная проблема оказываетсяо разделении какого-либо метода на два других метода и тестировании их по отдельности.К сожалению, эта аргументация вообще не поддерживается примером.Это не имеет ничего общего с двойником или ожиданиями, и вы могли бы оставить их без проблем.Вы хорошо знакомы с вашим кодом и своей проблемой, и это может показаться вам очевидным, но мне, как обычному читателю вашего вопроса, это не так.
Теперь о вашей проблеме: если вашапроблема действительно в том, что вы чувствуете, что разбивать report()
на две функции бессмысленно, потому что вы не можете протестировать output()
отдельно от gather_data()
, потому что gather_data()
необходимо вызвать для получения ввода для output()
, тогдавы забываете две вещи:
Разделение кода на отдельные функции - это всегда хорошая идея, если код можно логически разделить на эти функции.Это делает код более легким для понимания, более легким в обслуживании и более эффективным для тестирования.Последнее, потому что:
Даже если вы не можете проверить output()
отдельно от gather_data()
, вы все равно можете проверить gather_data()
отдельно.Этот тест даст вам более ранние предупреждения о меньшем куске кода, что облегчит идентификацию и решение проблем.
Связанная проблема, которую вы чувствуете, что не можете протестировать output()
отдельно от gather_data()
, не является проблемой, которая обычно решаема.В каждом случае у вас есть три варианта:
Вы высмеиваете буквальный ввод для output()
и тестируете его изолированным способом, основываясь на этом вводе (и обычно некоторых вариациях,для проверки нескольких путей кода).
Вы пишете фиктивную версию collect_data (), которая частично дублирует его логику, но гораздо проще рассуждать и вызывать ее для создания ввода для output()
.
Вы тестируете gather_data()
и output()
вместе, потому что прагматически слишком сложно тестировать output()
в изоляции.
gather_data()
требует ввода, и вы либо предоставляете его вручную, с помощью скрипта или кода, который все равно его производит.Последнее является вполне приемлемым прагматическим решением, при условии, что у вас есть отдельный тест gather_data()
, который уже сообщает вам, был ли сбой комбинированного теста из-за сбоя gather_data()
или из-за сбоя output()
.