Как стать автором
Обновить

Комментарии 39

Шёл 2018 год. В статьях о тестировании мы всё ещё читаем о том, почему нам нужно писать тесты.

а мне всё ещё приходится убеждать коллег в их пользе и выгоде от них… пока безуспешно ))) т.к. «писать тесты» — это «больше времени» и выход из зоны комфорта привычки
а заставить — нет полномочий ))
Смените место работы. Мой процесс очень походит на TDD, но чистым TDD не является, и всё же, я уже не представляю физически как можно писать код без тестов. Это всё-равно, что идти в бордель без полового органа.
ну, в «моих» проектах тоже всё «по TDD», а проекты у нас, к счастью (с этой точки зрения), по сути, поделены между разработчиками
так что боли я не испытываю — они сами едят этот кактус… просто мне хочется им донести лучшее, светлое…

Вам повезло. Я вот был в проекте, где написание модульных тестов было запрещено. Прямым указом. Место работы я в итоге сменил...

Как говорил Иисус Христос:
Не давайте святыни псам и не бросайте жемчуга вашего перед свиньями, чтобы они не попрали его ногами своими и, обратившись, не растерзали вас.
Если бы их большинство писало… Лично я узнал как правильно TDDшить в embedded только в этом году.
Было бы интересно прочитать:)
Лучше, чем в «Test-Driven Development for Embedded C», J.W.Grenning, я не напишу.

В оргинале «Отсутствие новых ключевых слов в конструкторе» звучало как «No new keyword in constructor». Я не специалист в PHP, на мне кажется, что тут имелось в виду, что не стоит создавать какие-либо объекты в конструкторе класса через new.

Про "статичные методы" уже был комментарий? Если нет, то вот.

Типичные ошибки:
1. Смешивают в кучу элементы разных ортогональных классификаций: по тестируемому объекта, по проверяемым характеристикам, по процессу тестирования.
2. Правильно замечают, что модульное тестирование проверяет конкретный код без зависимостей, и тут же предлагают не тестировать приватные методы, мол они протестируются косвенно.
3. Подменяют понятие «хорошей архитектуры» понятием «архитектура ориентированная на модульные тесты». Для второй характерно выпячивание всех кишок в публичный интерфейс с соответствующими неудобствами в использовании.
4. «Модульное тестирование — это ВЕСЕЛО!» — без конца мокать всё подряд — это совсем не весело.
5. Приводятся какие-тот странные ограничения на прикладной код вызванные тестами. Хотя реализация должна определяться требованиями. И это тесты должны подстраиваться под реализацию и уметь проверять любую, в том числе синглтоны, божественные объекты и пр.

Подробнее я расписал всё тут: habr.com/post/351430
А что не так с третьим пунктом, конкретно с нежеланием тестировать приватные методы? Ваши тесты не должны меняться, если вы приватную функцию отрефакторите в две (и соответствующим образом модифицируете вызовы), в этом как бы суть сокрытия этой логи, на то она и приватная
То, что модульные тесты — это тесты белого ящика, а тестирование публичного интерфейса — тестирование чёрного. Как вы протестируете, что, например, реализовали qsort правильно (и он не выродился в bubble из-за ошибки вычисления медианы) без доступа к приватной функции, реализующей одну итерацию?
Как вы протестируете, что, например, реализовали qsort правильно


Ключевым вопросом будет постановка термина «реализовали правильно». Приватной функции может и не быть вообще, как вы в таком случае проведете ваш тест?

В общем случае выглядит так, что поддерживать такие тесты дорого и сложность их эквивалентна сложности тестируемого кода.
модульные тесты — это тесты белого ящика

Тут важный момент — как проявляется знания тестов о тестируемом коде. Описанный вами случай, когда нам прям нужно знать что мы реализовали qsort, как по мне больше экзотика. В 99% случаев нас не должны интересовать такие детали.


Белый ящик в моем понимании — это дублирование реализации в тестах. Это высокая связанность тестов и тестируемого кода. Это очень хрупкие и в целом бесполезные тесты.


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


А вот тестирование приватных методов, "способа сортировки" и т.д. — это может быть полезно в процессе разработки, но затем если такой тест вдруг станет красным, я с большой долей вероятности просто его удалю.

если я подменяю зависимость, я не раскрываю деталей реализации для теста

Само существование этой зависимости — уже деталь реализации. А уж интерфейс этой зависимости и реализация стаба — и подавно. Да, вы можете вынести эту деталь в контракт. В результате контракт "сортировщика" внезапно начинает требовать на вход не только массив, но и "логгер", "квантайзер", "менеджер памяти", "хеш-функцию" и прочую ерунду, которую потом стыдливо прячут в IoC контейнерах. Создали себе проблему и героически её решили.


А вот тестирование приватных методов, "способа сортировки" и т.д. — это может быть полезно в процессе разработки, но затем если такой тест вдруг станет красным, я с большой долей вероятности просто его удалю.

Алгоритмическая сложность алгоритма сортировки — это прям требование, а не деталь реализации. Приёмочными тестами публичного интерфейса её никак не проверишь. Я даже больше скажу. Разделение интерфейса на "публичный" (доступный вообще всем за пределами класса) и "приватный" (недоступный вообще никому за пределами класса) — самое бестолковое, что только можно придумать. Разным потребителям должны быть доступны разные уровни интерфейсов. Через REST — одно, прикладному коду — другое, сериализатору — третее, менеджеру памяти — четвёртое, тестам — пятое, соседним классам в пакете — шестое, соседним пакетам в скоупе — седьмое. Сейчас же всё, что хоть кому-то нужно пихают в "паблик". Иногда сверху ещё адаптерами прикрывают, сужающими интерфейс.


Ну вот представьте, у вас есть один публичный метод и 10 приватных. Через "публичный" интерфейс тестировать — комбинаторный взрыв. Через "приватный" можно каскадно протестировать каждый модуль метод за линейное число тестов. Приватный метод для внешнего класса — деталь реализации, но для соседнего метода — внешний контракт.

В результате контракт «сортировщика» внезапно начинает требовать на вход не только массив, но и «логгер»


Не очень понимаю, откуда, например, в такой иерархии

interface Sorter { public function sort(array $array): array }

class MegaSorter implements Sorter 
{
   public function __construct(LoggerInterface $logger) {...} 

   public function sort(array $array): array {...}
}


появился в контракте логгер. В тестах вещей, зависящих от SorterInterface я этого никогда не увижу, потому что контракт остался простым.

Ну вот представьте, у вас есть один публичный метод и 10 приватных. Через «публичный» интерфейс тестировать — комбинаторный взрыв.


Если у вас до 10 приватных методов можно добраться разными комбинациями через комбинаторный взрыв, то это немножечко пахнет нарушением SRP

Через REST — одно, прикладному коду — другое, сериализатору — третее, менеджеру памяти — четвёртое, тестам — пятое, соседним классам в пакете — шестое, соседним пакетам в скоупе — седьмое


Ну тут можно подумать насчет того, что есть ISP. Но вообще опять попахивает нарушением SRP. На примере того же сортировщика можете продемонстрировать, когда такое нужно?
Не очень понимаю, откуда, например, в такой иерархии появился в контракте логгер.

Очевидно, вот тут:


public function __construct(LoggerInterface $logger) {...} 

Но судя по вопросу вы используете IoC контейнер, который создаёт вам объекты самостоятельно, скрывая эту часть интерфейса. Спрашивается, нафига выпячивали, чтобы потом скрывать? Смотрите, как то же самое делается без выворачивания кишок наружу:


interface Sorter { public function sort(array $array): array }

class MegaSorter implements Sorter 
{
   public function __construct() {
       $this->logger = AmbientContext.get(LoggerToken);
   } 

   public function sort(array $array): array {...}
}

Если у вас до 10 приватных методов можно добраться разными комбинациями через комбинаторный взрыв, то это немножечко пахнет нарушением SRP

Ну да, ну да, один файл с одним классом с 10 методами, нужными лишь одному из них, разбить на десять файлов с десятью классами по методу в каждом — архитектура офигеть как улучшится. Чем это принципиально отличается от "сделать все методы публичными" — ума не приложу.


Но вообще опять попахивает нарушением SRP.

99% разработчиков, молящихся на SRP вообще не представляют что такое Responsibility. Нет, SRP — это не про "каждому действию по отдельному классу", а про "не стирать цветное бельё с белым".


На примере того же сортировщика можете продемонстрировать, когда такое нужно?

Что именно? Разные интерфейсы разным потребителям? Я бы лучше послушал про случаи, когда всем потребителям нужен один и тот же интерфейс.)

Очевидно, вот тут:


Неочевидно. Конструктор не является частью контракта Sorter. Я могу использовать другой сортер с другим конструктором. Могу без него. Контракт стабилен.


public function __construct() {
$this->logger = AmbientContext.get(LoggerToken);
}


Вот за такие вещи я и не люблю фреймворки типа Yii. Это же откровенная лапша в коде. Если в контексте не будет этого токена, то весь код, особенно клиентские библиотеки, которые еще не обновились и почему то тоже его хотят — просто развалится. IoC с DI контейнером как раз таки позволяют вам вертеть зависимостями как вам угодно.

Ну да, ну да, один файл с одним классом с 10 методами, нужными лишь одному из них, разбить на десять файлов с десятью классами по методу в каждом — архитектура офигеть как улучшится


Ну, можно вообще отказаться от классов и сложить все функции в один файл с общей областью видимости памятью. Где-то я уже это видел, кажется это используется в процедурном программировании.

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

Я бы лучше послушал про случаи, когда всем потребителям нужен один и тот же интерфейс


Либо вы предлагаете мне показать вам GodObject, либо я не понял вашего вопроса.
Конструктор не является частью контракта Sorter.

Он является частью контракта MegaSorter.


Если в контексте не будет этого токена, то весь код, особенно клиентские библиотеки, которые еще не обновились и почему то тоже его хотят — просто развалится.

Не развалится, ибо будет использована реализация по умолчанию.


IoC с DI контейнером как раз таки позволяют вам вертеть зависимостями как вам угодно.

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


Ну, можно вообще отказаться от классов и сложить все функции в один файл с общей памятью.

Давайте без демагогии.


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

Какой именно параметр улучшится?

> начинает требовать на вход не только массив, но и «логгер», «квантайзер», «менеджер памяти», «хеш-функцию» и прочую ерунду

не обязательно, вопрос лишь в степени изоляции с которой нам удобно работать.

> это прям требование, а не деталь реализации.

а можете привести более… приближенный к реальности пример?

> Разным потребителям должны быть доступны разные уровни интерфейсов.

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

> Ну вот представьте, у вас есть один публичный метод и 10 приватных.

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

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


Причем тут модульные тесты и REST?

А при чём тут тесты? Тут мы обсуждали приватный и публичный интерфейсы. Тесты — один из пользователей кода, которому нужен соответствующий уровень доступа.

Нам нужно проверить, что алгоритм реализован правильно

мне сложно придумать кейс когда мне реально нужно проверить выбранный алгоритм. Ну то есть, у меня может задачи попроще, но у меня в критериях приемки не фигурирует "использовать алгоритм такой-то". Нефункциональные требования по производительности я всеравно буду хэндлить отдельными нагрузочными тестами.


А так комбинация из покрытия типами + примеры — этого достаточно что бы проверить требуемое поведение.


Результат сортировки останется прежним, но эффективность станет хуже чем даже у пузырька.

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


А если какой-то вариант кода будет лучше оптимизироваться JIT-ом, вы тоже будете в модульных тестах проверять это дело? или это уже перебор?


Тут мы обсуждали приватный и публичный интерфейсы.

Скорее то, сколько знаний о реализации мы экспоузим в тэстах. И я не очень понимаю причем тут http api и модульные тесты. Для приемочных/интеграционных тестов да, можно говорить. Модульные действуют на другом уровне.


Да и те же http api можно частично покрыть типами что бы уменьшить необходимость в дорогих тестах.

Меня всегда поражало это двоемыслие. Если рядом лежит приватный метод, то его тестировать не надо, мол протестируется косвенно. Но стоит этот метод вынести в отдельный класс и внезапно тестировать его необходимо отдельно, даже если он больше нигде не используется. Плюс замокать его в сходном классе, чтобы (не дай бог!) не протестировать его ещё и косвенно. А причина проста до безобразия — дизайн языка такой, что в нём сложно тестировать приватные методы.
Но стоит этот метод вынести в отдельный класс и внезапно тестировать его необходимо отдельно

Я такого не говорил, я так же могу проверять его косвенно. Зависит от необходимого для меня уровня изоляции (и опять же причин по которым я вынес это в отдельный класс).


что в нём сложно тестировать приватные методы.

они не спроста приватные (хотя часто просто так, да, или псевдо-приватные). Это деталь реализации которая не важна с точки зрения клиентского кода. Давайте так. Вот есть у нас функция и она спрятана в контексте модуля. Вы вооще никак не можете получить к ней доступ. Будете ли вы экспортировать функцию или будете проверять ее косвенно? Или в целом вопрос некорректен без указания контекста что мы тестируем, с какой целью и т.д.?

Я сторонник энкапсуляции, но не сокрытия. Клиентскому коду виднее важна она для него или нет. По возможности я буду тестировать требования к модулю (то есть косвенно), но если там какая-то сложная логика, то протестирую сначала эту функцию, а потом уже то, что от неё зависит.

> Я сторонник энкапсуляции, но не сокрытия.

можете раскрыть столь глубокую мысль? То есть information hiding это бушит? Или вы про то что кастыль вида private/public это булшит? Если последнее — то я вас поддержу.

> но если там какая-то сложная логика, то протестирую сначала эту функцию

И скорее всего я буду делать так же. И из моего опыта — это крайне редкие случаи.
И про то и про то в принципе.

Ну смотря как трактовать information hiding.

Из того что вы говорите, вам в целом важно скрыть как работает модуль в том плане, что бы изменения в этом модуле не оказывали каскадного эффекта на клиенский код. Так? Типа low coupling и все такое. И для тестов вы просто делаете исключение (и то стараетесь не делать, если конечно так не проще), дабы упростить себе жизнь.

Я все верно уловил?
Ну типа того, да.
2. не просто «без зависимостей» а «в изоляции». Иногда (в случае сложных алгоритмов) это подразумевает тестирование приватных методов, но это крайне редко. Опять же основная рекомендация для таких тестов — нещадно их удалять когда они начинают мешать.

3. Тут опять же подмена понятий. «хорошая архитектура» с точки зрения модульных тестов — это грамотное разбиение системы на эти самые модули. Изоляция поведения. Декомпозиция. Модульные тесты тут позволяют проверить качество разделения простым способом. Если при изменении у вас падает много тестов или вы не можете быстро понять что именно сломалось, то значит либо код разбит не очень удобно либо тесты не очень то и изолированные. А кишки наружу — это как хотите так и будет.

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

5. тут я с вами частично согласен. Частично потому что тесты — это клиентский код. Они должны помогать формировать удобные контракты, которые мы сможем потом использовать. Во всяком случае я нахожу наибольшую ценность модульных тестов именно в этом — формирование контрактов и изоляция.

К вашей статье добавлю ссылку где в целом все эти тезисы разбираются: [J B Rainsberger — Integrated Tests Are A Scam](https://www.youtube.com/watch?v=VDfX44fZoMc)
не просто «без зависимостей» а «в изоляции».

Когда говорят "без зависимостей" подразумевается "без реальных зависимостей", то есть "в изоляции от реальных зависимостей". Давайте без терминологических споров.


Опять же основная рекомендация для таких тестов — нещадно их удалять когда они начинают мешать.

Я вам больше скажу — любые тесты надо удалять, если они мешают больше, чем помогают.


Тут опять же подмена понятий. «хорошая архитектура» с точки зрения модульных тестов

  • это и есть "архитектура ориентированная на модульные тесты". То есть архитектура, где удобно изолировать каждый модуль от остальных. В реальном приложении удобство изоляции отдельного модуля — не имеет практической ценности. Эта фича нужна лишь модульным тестам. А вот удобство изоляции подсистем — часто имеет. Но это не про модульное тестирование. Так что не подменяйте понятия, "архитектуру ориентированную на модульные тесты" называя "хорошей" — это вводит в людей в заблуждение.

Интеграционные тесты — это смазывать фокус внимания.

Почитайте, пожалуйста, мою статью не по диагонали. Очень вас прошу, я же так старался. Интеграционные тесты не противопоставляются модульным, а дополняют их. А вот компонентные тесты противопоставляются тандему модульных и интеграционных.


выставлять прекондишен в интеграционных или в модульных тестах. По трудозатратам в целом модульные мне кажутся более выгодными.

У меня сейчас перед глазами два проекта:


  1. Применяется юнит тестирование. Перед каждым тестом идёт создание стабов, шпионов и прочего непотребства, необходимого для инстанцирования и проверки класса. Часто код самого теста существенно меньше кода подготовки. При этом тесты получаются сильно завязанными на детали реализации — ведь все зависимости надо замочить и прочекать, что обращения к ним происходят правильно. Интеграционных тестов нет вообще, ибо долго, сложно и другие отговорки.


  2. Применяется компонентное тестирование. Мока всего два: http и location. Регистрируются они один раз для всех тестов. Сами тесты весьма просты и не требуют сложной подготовки. При этом покрытие кейсов как у модульных с интеграционными вместе взятыми. Что не мешает точно понимать в каком модуле проблема, ибо компонентное тестирование идёт каскадом.

К вашей статье добавлю ссылку где в целом все эти тезисы разбираются

Наоборот, в моей статье разбираются тезисы из этого выступления. Человек там говорит экспрессивно, лозунгами, но, к сожалению, не потрудился как следует разобраться в теме. Называть приёмочные системные тесты интеграционными — это уже за гранью добра и зла.

Давайте без терминологических споров.

ну так вы же их разводите.


В реальном приложении удобство изоляции отдельного модуля — не имеет практической ценности.

Декомпозиция не имеет практической пользы?


У меня сейчас перед глазами два проекта:

увы по вашему описанию я могу сделать два вывода:


  • слишком много зависимостей?
  • так себе ситуация в JS с mock фреймворками?

У меня же перед глазами другая проблема. Прекондишен для функционала на уровне базы (я ж еще и бэкэндщик, мне не надо мокать http, мне надо как-то стэйт в базе держать). И знаете, вот можете что хотите говорить но я интеграционные тесты буду использовать только для проверки контрактов которые мне не принадлежат.


как следует разобраться в теме.

может быть вы как следует не разобрались в том о чем он вещает и только лозунги слушали? Да, я согласен что сам по себе доклад в целом не отвечает на вопрос "как делать" и для этого лично мне пришлось немало времени потратить что бы понять "как так он живет хорошо с модульными тестами". Ну то есть вопрос о e2e тестах скажем у него за кадром остался, вопрос о event driven подходах и снижении количества зависимостей до минимума так же. Но все же...

удобство изоляции отдельного модуля — не имеет практической ценности.

Декомпозиция не имеет практической пользы?

Изоляция и декомпозиция — ортогональные понятия. Впрочем, декомпозиция, ради декомпозиции, тоже не имеет практической ценности.


слишком много зависимостей?

Да вроде не слишком.


так себе ситуация в JS с mock фреймворками?

Какая разница через что мокать, если в одном проекте это надо делать перед каждым тестом, а в другом — вообще не надо?


У меня же перед глазами другая проблема. Прекондишен для функционала на уровне базы

Я не очень понял суть проблемы. Ну, поднять in-memory базу не такая большая сложность. Зато точно будете уверены, что ваш запрос базой будет интерпретирован верно.


лично мне пришлось немало времени потратить что бы понять "как так он живет хорошо с модульными тестами".

Предлагаю потратить ещё немного времени, чтобы понять "как так он живёт хорошо с компонентными тестами" :-)

Изоляция и декомпозиция — ортогональные понятия.

Не согласен. У этих понятий есть пересечение. И это важно.


Впрочем, декомпозиция, ради декомпозиции, тоже не имеет практической ценности.

согласен.


Ну, поднять in-memory базу не такая большая сложность.

проблема с подготовкой различных прекондишенов для различных сценариев (в том числе и негативных). По объему кода разницы с модульными тестами не особо много. По степени хрупкости — так же. По скорости работы — разница огромная. По тому, сколько времени нужно для определения причины "красных тестов" — изолированные тесты тоже выигрывают.


что ваш запрос базой будет интерпретирован верно.

я тестирую запрос или то как база интерпритирует запросы? Или тот факт что под выбранны прекондишен запрос вернул то что мне нужно? Сколько мне нужно таких примеров сделать что бы убедиться в корректности системы? хочу ли я это делать или я больше доверюсь комбинации из приемочных e2e тестов на позитивные сценарии (потому что они всеравно будут триегрить те же запросы, просто на меньшем количестве возможных прекондишенов) + ограничения на уровне схемы + тайп чекинг.


И я буду намного больше уверен что все работает.


"как так он живёт хорошо с компонентными тестами"

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


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


И очень важный момент — когда я говорю изолированные тесты я не имею ввиду что я всегда изолирую все зависимости и т.д. Как никак стандартную библиотеку я ж не подменяю, что мешает мне еще что-то не подменять? Но тесты все еще изолированы на уровне модуля который я хочу тестировать.

Не согласен. У этих понятий есть пересечение. И это важно.

Какое пересечение есть у нарезки хлеба и упаковки его в пакеты?


проблема с подготовкой различных прекондишенов для различных сценариев (в том числе и негативных).

А в чём там проблема?


// Создали статью от тест-админа
const article = habr.createArticle({ title: 'Hello' })

// Зарегались под новым пользователем с рандомным уникальным именем
const person = habr.signUp({ name : stub.person.name , password: 'bar' })

// попытались удалить
shouldFail( ()=> article.remove() , new Error( 'You do not have rights to delete "Hello" article' ) )

По объему кода разницы с модульными тестами не особо много.

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


По степени хрупкости — так же.

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


По скорости работы — разница огромная.

Зависит от инструментария. Для примера — TestBed в Ангуляре, инициализация которого занимает 0.2с на топовой рабочей станции. Так как в модульных тестах нужно без конца подсовывать свои моки, то TestBed ресетится перед каждым тестом. Получается 30 тестов занимают минимум 5 секунд. С компонентными тестами, его достаточно один раз настроить перед тестами и далее не трогать, что позволяет вырубить авторесет и ускорить тесты в 10 раз. Вот тебе и "быстрые" модульные тесты.


По тому, сколько времени нужно для определения причины "красных тестов" — изолированные тесты тоже выигрывают.

Покраснел модульный тест, проблема может быть:


  • в тесте
  • в модуле
  • в моке

Покраснел компонентный тест, проблема может быть:


  • в тесте
  • в модуле
  • в плохо протестированной зависимости, модульный тест тут бы просто выдал зелёную галочку

я тестирую запрос или то как база интерпритирует запросы?

Зависит от того хотите ли вы чтобы у вас правильно запросы генерились или же чтобы приложение работало. Вы можете не знать все тонкости синтаксиса, в СУБД может быть бага, недокументированное ограничение. Виноваты-то может быть и разработчики СУБД, но проблема-то ваша. И чем раньше вы узнаете о ней, тем лучше.


у вас не так много публикаций и комментариев в сети что бы можно было это проследить.

А сколько надо, чтобы выглядеть авторитетом?


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

SPA в основном.


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

Да компонентные готовят обычно не правильно. Например, порядок запуска компонентных тестов важен, в отличие от модульных, которые можно запускать в любом порядке. Если компонентные запускать в неправильном порядке, то появляются проблемы, о которых вы говорили, что сложно понять кто виноват.


Как никак стандартную библиотеку я ж не подменяю

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

В книге "чистая архитектура" предлагается тестировать api библиотек, которые вы используете, чтобы знать, что новое обновление не изменило её интерфейс и чтобы лучше понимать её работу.
Что вы думаете об этом?

На моё сознание аргументы автора легли с немного иными приоритетами, а именно: чтобы быть уверенным, что твоё понимание "как оно работает и что делает" соответствует реальности и чтобы иметь конспект заведомо достоверных примеров.


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

Зарегистрируйтесь на Хабре, чтобы оставить комментарий