Pull to refresh
4
0
Антон Бобров @bvrmn

Седой питонист

Send message
Не позволяют, потому что пишется очень много кода и очень много тест кейсов практически одинаковых

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

> Если у нас есть, допустим, компонента которая делает расчет и записывает его в БД

Это две компоненты в чистом виде) Независимые требования.
Были ли тесты хорошим кодом? Содержали ли они дублирование?

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

Почему одна компонента не может быть деталью реализации другой?

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

> а как сделать «так»?

Увы, к формальному ответу индустрия еще не пришла. Думать и пробовать, пытаться ужимать интерфейсы до минимума, меньше входных точек — меньше вещей, за которыми нужно следить.
Что такое компонента?

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


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


В статье призывается не создавать новых тестов когда мы выносим что-то в отдельный класс. Этот отдельный класс это компонента или нет?

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

Количество тестов растет лавинообразно, если все тестировать через внешний интерфейс, и не тестировать зависимости по отдельности.

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

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

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

Например, возьмем rate limiting. У нас есть объект который нужно лимитировать, период времени, количество вызовов. Хорошей реализацией будет просто голый redis + интерфейс с элементарными вызовами. Знает ли redis про нашу предметную область? Да нет конечно, но при этом он хорошая реализация.
И структура тестов и структура кода должна быть отражением требований.

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

это BDD

BDD крутится вокруг специального DSL для написания сценариев и отражающего предметную область. BDD ближе к интеграционному тестированию, потому что, как правило, в сценариях затрагивается сразу несколько компонент. Пользователь залогинился, добавил товар в корзину и зачекаутил корзину. BDD скорее инструмент для приемочного тестирования, а не инструмент разработчика.


Юнит тесты — все же должны быть изолированными тестами.

Кому должны?) В статье раскрыта суть проблемы такого подхода. Юнит тесты, которые тестируют отдельные приватные функции, только добавляют проблем.


Покрытие компоненты/модуля через публичный API позволяет эффективно проводить рефакторинги в дальнейшем.

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

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

Тесты могут стать сложными, только по двум причинам:


  1. В случае сложного публичного интерфейса
  2. Внутри есть обращение к внешним сервисам. Тогда наивный простой интерфейс приведет к хаосу из моков и пронизывающего все и вся DI. Как правило, такие обращения можно подтянуть поближе к интерфейсу и сделать все понятнее.

В любом случае, сложные тесты сигнализируют о сложном дизайне основного кода.

Статьи Дяди Боба в форме диалога это скорее притчи, которые наталкивают на правильные мысли. Они не про практичность. У меня каждый такой диалог закреплял интуитивный опыт, «знаю, а сказать не могу», в виде набора формализмов, которыми уже можно пользоваться и ссылаться при принятии решений.
Тот факт, что у нас нет тестов на вспомогательные классы, которые используются в Х, означает просто, что в этих классах нет логики достаточно сложной для того, чтобы ее стоило тестировать отдельно.
В этом весь смысл, тестирование поведения через публичный интерфейс, автоматически должно трогать все внутренности.

Как только они усложнятся — появятся отдельные тесты на них (просто потому, что тестировать их через общий X Api непрактично.
Они усложняются как следствие дополнительных требований к X API, и тестировать их естественно через X API. Об этом в статье и говорится.

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

Пример из жизни: недавно менял реализацию в библиотеке, патч на 1.5к строк, ни один тест из 152 штук не пострадал, так как это было полное переписывание только внутренних функций. Если бы не было тестов или они тестировали конкретные «юниты» — никогда бы не взялся за такой рефакторинг.

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity