Pull to refresh

Comments 11

Пример специально упрощен для понимания проблемы и мой код сильно запутанней этого. Если бы createPost и addTagToPost лежали в стороннем модуле то мы могли использовать что-нибудь вроде jest для того чтобы перехватить обращения к ним, но в нашем случае это не решение задачи поскольку функции, вызовы которых мы хотели бы перехватить, могут быть скрыты глубоко внутри scope тестируемой функции и не экспортированы наружу.

Почему на этом месте нельзя перестать писать лапшу, и сделать рефакторинг?
А как гарантировать, что новый код будет вести себя хотя бы отдалённо похожим образом?
Интересно, попробую.

Возможно вам будет любопытно поглядеть на похожие проекты:
https://github.com/speedskater/babel-plugin-rewire
https://github.com/asapach/babel-plugin-rewire-exports
У них есть один общий минус: они ломают sinon.js в некоторых случаях (в подробностях не было времени разобраться).

Ваня, но ведь у тебя тесты ничего не тестируют, ты вызываешь и проверяешь какую-то левую переменную, в этом и проблема. Покрывай результат от входных параметров, а как там внутри реализовано, тебя должно мало волновать, максимум для создания моков.


В итоге использовав Snare, получился тест, который развалится при рефакторинге createPostWithTags из-за жесткой зависимости от его реализации.


P.S. Я понимаю, что есть разные подходы реализации тестов, но всё же советовал рассматривать код/методы как «черный ящик» (конечно критически оценивая каждый случай).

> максимум для создания моков
Никак не противоречит моему подходу

>В итоге использовав Snare, получился тест, который развалится при рефакторинге createPostWithTags из-за жесткой зависимости от его реализации.
Костя, в моем кейсе это не работает, к сожалению.

Кстати, «в моем кейсе это не работает» — это проблема многих статей, в итоге пример только сбивают с толку и вызывают недоумение, надо как-то показать пример, например от простого, к сложному, мол «смотрите, а тут всё, никак, только Snare»

Такой тест в этом примере для того чтобы показать что вообще можно делать
С использованием babel-rewire единственное, что нельзя оттестировать (на нашем проекте) — методы, которые пишут в window.location — phantom.js например не дает тесту возможности заглушить такое, и исполнение теста рушится.
Ваня, привет. Спасибо за статью. Хочу высказать свое мнение.

но всё же советовал рассматривать код/методы как «черный ящик»

RubaXa подписываюсь под каждым словом.

Считаю исключительным заблуждением тестирование работоспособности модулей вышеописанным способом.

Тесты являются потребителем вашего кода, им важен контракт, соответствие заявленных способностей, описанных в интерфейсе (сигнатуре) реальным действиям.

Тестироваться должны исключительно публичные методы, никаких приватных методов, а тем более то, что спрятано в замыканиях (привет, rewire). Почему так? Все просто, мы тестируем работоспособность модуля, а не его реализацию, тестирование приватных данных чревато серьезной завязкой на реализацию, при малейшем изменении все упадет. Такие тесты ничего не тестируют, они проверяют, что код не поменялся. Тестирование исключительно публичных методов дает серьезный маневр для рефакторинга или полного изменения реализации, достаточно оставить сигнатуру публичных методов нетронутой, а тесты проверят, что все по-прежнему работает ожидаемо.

Теперь конкретно про ваш случай.

Поскольку createPostWithTags() изпользует createPost() и addTagToPost() и зависит от результата выполнения этих функций

Когда вы пишете тест — такие рассуждения не совсем корректны. На мой взгляд, правильно рассуждать так:

Когда мы вызываем createPostWithTags у нас должно отправиться 2 post-запроса, один на создание поста, другой на добавления тега. Все, больше никаких рассуждений, тем более о внутреннем устройстве модуля.

Любой тест должен быть определен по правилу: даю данные на вход, получаю ожидаемый результат (сайд-эффект) или ожидаемые данные на выходе.

Чтобы это сделать нам приходится дублировать тест из самого createPost()

Кстати, вот еще интересный момент. Я глубоко убежден, что к написанию тестов нужно подходить совсем иначе, нежели к написанию кода продукта.

Код тестов должен быть максимально декларативным, должен содержать минимум логики, и копипаста из других тестов — это вполне ок. Цена ошибки в логике теста очень высока, потому ее надо сводить к минимуму.
Когда мы вызываем createPostWithTags у нас должно отправиться 2 post-запроса, один на создание поста, другой на добавления тега.

Вот этого мы как раз и не знаем. Мы знаем, что api() должен вызываться 2 раза с разными параметрами. Может, api там внутри выполняет кэширование (и не делает лишних запросов). Может, он делает повтор при ошибке. Может, вообще преобразует данные из транспортного формата в формат клиентского кода.


Поэтому все равно придется что-то мокать. Не createPost() — так api().

Only those users with full accounts are able to leave comments. Log in, please.