Как реализовать модульный тест метода содержащего создание экземпляра SoapClient?
Внутри собственного класса использую SoapClient для обмена данными с внешним источником. Чтобы было удобнее тестировать изолировал создание экземпляра SoapClient в виде отдельного метода getSoapClient. Внутри него создаю объект класса SoapClient передавая в конструктор определенный набор параметров, в том числе url внешнего сервиса.
Далее по коду для написания unit-тестов мокаю результат метода getSoapClient и все получается, как нужно...
Вопрос в том, как написать unit-тест для самого метода getSoapClient, который фактически состоит из одной строчки return new SoapClient($url, $params);
Мне нужно проверить, что на вход конструктора подается именно тот url, который нужно, и используются правильные параметры для инициализации...
В Инет все говорят копать в сторону dependency injection, но мне в данном случае неудобно получать "из вне" уже готовый объект, плюс все равно где-то в другом месте кода нужно будет проверять параметры его инициализации...
Дмитрий, Так мне и нужно только тип проверить и параметры в конструкторе, но когда я передаю любой url, а передачу url мне нужно проверить тоже, то при создании объекта он сразу лезет на этот url и выкидывает exception если не получается!
Немного непонятно что именно вы хотите тестировать в данном методе.
Это типичный humble object, который юнит-тестами покрывать не требуется. Суть в том, чтобы сделать такие сложнотестируемые места настолько простыми, насколько это в принципе возможно и свести вероятность ошибки в них к минимальной.
Этот код может (и желательно чтобы был) быть проверен интеграционным или приемочным тестом, он за рамками юнит-тестирования.
Я уже писал выше, что я хочу проверить (именно на этапе unit-тестирования), что
На выходе метода getSoapClient действительно объект класса SoapClient.
Аргумент $url, переданный на вход getSoapClient был использован в качестве url при вызове конструктора SoapClient.
При вызове конструктора SoapClient были использованы именно те параметры, которые были захардкодены в методе getSoapClient.
Интеграционные и другие высокоуровневые тесты это конечно хорошо, но эти утверждения вполне можно и нужно (IMHO) проверить на этапе unit-тестирования. Эти требования достаточно "формальны" для этого, но проверка соблюдения их довольно важный этап тестирования... Поправьте меня если это не так.
Тестирование - не самоцель, а средство обеспечения качества программы. Humble Object (вы же прочитали статью по ссылке?) именно об этом. Вы никак не можете проверить что передано в конструктор. Просто потому что конструктор невозможно замокать. Никак. Ничем. Что-нибудь магическое из мира PHP не рассматриваю.
Дальше. Если рассматривать TDD, главный принцип которого "не писать код без упавшего теста". TDD не утверждает что упавший тест должен быть обязательно модульным. Интеграционные и приемочные тесты - точно такая же часть TDD, как и модульные. См. пирамида тестирования или Р. Мартина "Идеальный программист", главу 8 "Стратегии тестирования"
Михаил, статью прочитал, спасибо. Правда я не уверен, что описанный в ней случай, это мой вариант... Я ведь не хочу тестировать работу или какие-то внутренности SoapClient, а хочу только проверить соответствие фактических аргументов моим ожиданиям...
По поводу невозможности тестирования конструкторов (в общем случае) не соглашусь. У меня много тестов, где я тестирую прикладной код через AspectMock, в том числе и конструкторы... Я их успешно мокаю, но опять же для того, чтобы проверить соответствие параметров ожидаемым.
Проблема в том, что сейчас это не прикладной код, а системная библиотека, которую я замокать не знаю как...
Сейчас пришла мысль, добавить еще один метод, который бы возвращал наименование класса SoapClient, как строку. Тогда можно отдельно проверить такой метод (эту строку) и, подменив конструктор тестовым, потом проверить и его. Правда мне кажется, что это уже "тестовое извращение"... ;)
Антон Шаманов, я имел ввиду, что url передается аргументом в функцию getSoapClient и нужно убедиться, что именно он и без изменений был передан в соответствующий аргумент конструктора SoapClient, а остальные аргументы этого конструктора захардкодены и их тоже нужно провалидировать.
Антон Шаманов, вы слишком радикальны в суждениях... ;)
Если мне нужно протестировать функцию/метод, которая возвращает результат, то обычно я использую один из двух вариантов.
Первый, если вызываемая функция "безопасна для теста" (не лезет в сеть или бд и т.п.), то просто вызываю ее и анализирую детально результат. Обычно, передаваемые на вход такой функции параметры, как-то учитываются в результате, например, записываются в структуры возвращаемого объекта, иначе какой смысл вообще в этих аргументах, если они не влияют никак на результат. Правда в этом случае модульный тест получается не вполне "модульным"...
Второй, я мокаю вызываемую функцию и проверяю все аргументы на входе и возвращаю на выходе "формально-корректный" результат, который проверяю на выходе метода-обертки.
В большинстве случаев мне этого вполне хватает. Правда в описанной задаче, оба этих приема не подходят...
Это вопрос из серии "стоит ли вообще писать автосты и если Да, то в каком объеме"...
Лично я никогда не сомневался в необходимости писать автотесты, но, до недавнего времени, у меня просто не было на это время, т.к. конкретный заказчик не хотел оплачивать время на их разработку... В текущем проекте ситуация другая, очень много вариативной бизнес-логики, большинство из которой сложно проверить из вне (я имею ввиду внешний интерфейс), либо просто нет физической возможности проверять все варианты при каждом изменении... А нужно проверять ВСЕ и при КАЖДОМ, т.к. практика показала, что даже незначительное изменение в одном месте программы может "привести к проблеме" в совсем другом... Поэтому остаются только автотесты разных уровней.
С другой стороны, если автотесты не покрывают 100%, как минимум, нового кода, то ценность их весьма сомнительна (IMHO)... Получается ты тратишь не мало времени на написание тестов, но их успешное прохождение не гарантирует в общем случае отсутствие ошибок... Тогда зачем их писать вообще?
Это вопрос из серии "стоит ли вообще писать автосты и если Да, то в каком объеме"...
бред
у тебя есть метод A::run() для которого написаны тесты, и метод-обертка B::run(). Лично я не вижу никакого практического смысла в написании тестов для B::run() т.к. данные передаются в A::run() без изменений.
Антон Шаманов, если метод-обертка делает только вызов другого метода, то согласен, но в данном случае это не так. Даже в вашем случае, если внутри одного метода только вызов другого, то возможно есть какие-то комбинации-преобразования в аргументах метода-обертки и "проксируемого метода", иначе я не вижу вообще никакого смысла в методе-обертки! Если же такие преобразования есть, например, захардкодены какие-то передаваемые аргументы, то важно убедиться в их правильности...
А по поводу "бреда", что именно вы имеете ввиду? Необходимость 100% покрытия или что?
Вопрос в том, как написать unit-тест для самого метода getSoapClient, который фактически состоит из одной строчки return new SoapClient($url, $params)
А по поводу "бреда", что именно вы имеете ввиду? Необходимость 100% покрытия или что?
это не вопрос из серии "стоит ли вообще писать автосты", это вопрос из серии "стоит ли писать тесты ради галочки?". 100% покрытие тестами не гарантирует, что сами тесты покрывают методы полностью.
иначе я не вижу вообще никакого смысла в методе-обертки!
1. короткий вызов
2. инкапсуляция - в классе наследнике нужно будет переопределить только один метод (при необходимости для него может быть создан тест)
Что конкретно не так? Как переформулировать?
Если вы поняли суть вопроса, то может быть подскажете вариант решения, а не просто будете писать "общие фразы"?
это не вопрос из серии "стоит ли вообще писать автосты", это вопрос из серии "стоит ли писать тесты ради галочки?". 100% покрытие тестами не гарантирует, что сами тесты покрывают методы полностью.
Если тесты написаны достаточно качественно и на разных уровнях (модульные, интеграционные и т.д.), то 100% покрытие дает достаточную уверенность в правильности работы приложения. Если тесты написаны формально, только чтобы соблюсти условие 100% покрытия, то они не только бесполезны, но и вредны...
Если честно, то я не получаю особого удовольствия от траты времени на написание тестов, особенно если кусок кода кажется настолько тривиальным, что он просто не может работать не так, как нужно... И при этом уже не один раз ловил сам себя на том, что кусок из трех строчек не покрытый или некачественно покрытый тестами, может развалить всю систему! Причем это может быть банальная опечатка или ошибка при разрешении конфликта при сложном merge...