Каков наилучший способ модульного тестирования защищенных и закрытых методов в Ruby с использованием стандартной Test::Unit
среды Ruby ?
Я уверен, что кто-то непременно возьмет трубку и будет догматически утверждать, что «вы должны только использовать общедоступные методы модульного тестирования; если это требует модульного тестирования, это не должен быть защищенный или частный метод», но я не очень заинтересован в обсуждении этого. У меня есть несколько методов, которые являются защищенными или частными по уважительным и обоснованным причинам, эти частные / защищенные методы являются умеренно сложными, и публичные методы в классе зависят от правильного функционирования этих защищенных / частных методов, поэтому мне нужен способ для тестирования Защищенные / частные методы.
Еще одна вещь ... Я обычно помещаю все методы для данного класса в один файл, а модульные тесты для этого класса - в другой файл. В идеале я хотел бы, чтобы вся магия реализовала эту функциональность «модульного теста защищенных и закрытых методов» в файле модульного теста, а не в основном исходном файле, чтобы сделать основной исходный файл максимально простым и понятным.
источник
Ответы:
Вы можете обойти инкапсуляцию с помощью метода send:
Это «особенность» Ruby. :)
Во время разработки Ruby 1.9 были внутренние дебаты, в которых рассматривалось
send
уважение к частной жизни и ееsend!
игнорирование, но в итоге ничего не изменилось в Ruby 1.9. Игнорируйте комментарии ниже, обсуждаяsend!
и ломая вещи.источник
send!
вещь, она была отменена давно,send/__send__
может вызывать методы любой видимости - redmine.ruby-lang.org/repositories/revision/1?rev=13824public_send
(документация здесь ), если вы хотите соблюдать конфиденциальность. Я думаю, что это плохо для Ruby 1.9.Вот один простой способ, если вы используете RSpec:
источник
Просто заново откройте класс в своем тестовом файле и переопределите метод или методы как общедоступные. Вам не нужно переопределять внутренности самого метода, просто передайте символ в
public
вызов.Если ваш оригинальный класс определен так:
В тестовом файле просто сделайте что-то вроде этого:
Вы можете передать несколько символов,
public
если хотите показать больше частных методов.источник
instance_eval()
может помочь:Вы можете использовать его для прямого доступа к закрытым методам и переменным экземпляра.
Вы также можете рассмотреть возможность использования
send()
, что также даст вам доступ к закрытым и защищенным методам (как предложил Джеймс Бейкер)Кроме того, вы можете изменить метакласс вашего тестового объекта, чтобы сделать приватные / защищенные методы общедоступными только для этого объекта.
Это позволит вам вызывать эти методы, не затрагивая другие объекты этого класса. Вы можете снова открыть класс в своем тестовом каталоге и сделать его общедоступным для всех экземпляров вашего тестового кода, но это может повлиять на ваш тест открытого интерфейса.
источник
Один способ, которым я делал это в прошлом:
источник
Вы также можете преобразовать их в новый объект, в котором эти методы являются общедоступными, и делегировать им конфиденциально в исходном классе. Это позволит вам тестировать методы без магической метарубы в ваших спецификациях, сохраняя при этом их конфиденциальность.
Каковы эти веские причины? Другие языки ООП могут вообще обходиться без частных методов (вспоминается smalltalk - когда частные методы существуют только как соглашение).
источник
Подобно ответу @ WillSargent, вот что я использовал в
describe
блоке для особого случая тестирования некоторых защищенных валидаторов без необходимости проходить через тяжелый процесс их создания / обновления с помощью FactoryGirl (и вы можете использоватьprivate_instance_methods
аналогично):источник
Чтобы сделать общедоступным защищенный и приватный метод для описанного класса, вы можете добавить следующее в ваш spec_helper.rb и не трогать какие-либо из ваших спецификационных файлов.
источник
Вы можете «открыть» класс и предоставить новый метод, который делегирует частному:
источник
Я бы, вероятно, склонялся к использованию instance_eval (). Однако прежде чем я узнал о instance_eval (), я бы создал производный класс в своем файле модульного теста. Затем я бы установил приватный метод (методы) как общедоступный.
В приведенном ниже примере метод build_year_range является закрытым в классе PublicationSearch :: ISIQuery. Получение нового класса только для целей тестирования позволяет мне устанавливать методы, которые будут общедоступными и, следовательно, непосредственно тестируемыми. Аналогично, производный класс предоставляет переменную экземпляра с именем 'result', которая ранее не была представлена.
В моем модульном тесте у меня есть тестовый пример, который создает экземпляр класса MockISIQuery и напрямую тестирует метод build_year_range ().
источник
В Test :: Unit framework можно написать,
Здесь "method_name" является закрытым методом.
И во время вызова этого метода можно написать,
источник
Вот общее дополнение к классу, который я использую. Это немного больше ружья, чем просто обнародование метода, который вы тестируете, но в большинстве случаев это не имеет значения, и это гораздо более читабельно.
Использование send для доступа к защищенным / приватным методам не работает в 1.9, поэтому это не рекомендуемое решение.
источник
Чтобы исправить верхний ответ, приведенный выше: в Ruby 1.9.1 это Object # send, который отправляет все сообщения, и Object # public_send, который уважает конфиденциальность.
источник
Вместо obj.send вы можете использовать одноэлементный метод. Это еще 3 строки кода в вашем тестовом классе и не требует никаких изменений в реальном коде для тестирования.
В тестовых случаях вы затем используете
my_private_method_publicly
всякий раз, когда вы хотите проверитьmy_private_method
.http://mathandprogramming.blogspot.com/2010/01/ruby-testing-private-methods.html
obj.send
для приватных методов был заменен наsend!
1.9, но позжеsend!
был удален снова. Такobj.send
работает на отлично.источник
Я знаю, что опаздываю на вечеринку, но не проверяйте частные методы ... Я не могу придумать причину сделать это. Публично доступный метод где-то использует этот приватный метод, тестирует публичный метод и множество сценариев, которые могут привести к использованию этого приватного метода. Что-то входит, что-то выходит. Тестирование приватных методов - это большое «нет-нет», и это усложняет процесс рефакторинга вашего кода позже. Они являются частными по причине.
источник
Для этого:
Вы можете реализовать это в своем файле test_helper:
источник
Disrespect
вPrivacyViolator
(: P) и заставилdisrespect_privacy
метод временно редактировать привязку блока, чтобы напомнить целевой объект для объекта-оболочки, но только на время блока. Таким образом, вам не нужно использовать параметр блока, вы можете просто продолжить ссылаться на объект с тем же именем.