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

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

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

Более одной ответственности

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

Разрабатываемый вами класс или модуль должен иметь только одну обязанность. Это означает, что класс должен делать только одну вещь или вещи, связанные с этой вещью, но не больше… и не меньше. И важное замечание "и не меньше". Если вы разделите ответственность на несколько классов или модулей, вы можете получить код равиоли слишком быстро.

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

Избегайте взаимозависимостей

Взаимозависимость создается, когда модулю нужны функции второго модуля, а второму нужны функции первого.

Зависимости всегда должны быть вертикальными. В случае, если нам нужны функциональные возможности, которые неясно, относятся ли они к модулю или другому, возможно, будет хорошей идеей создать weak-module, содержащий эту новую функцию, и оставить его ниже в иерархии зависимостей.

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

Избегайте слишком фрагментации между пакетами

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

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

В нашем примере мы можем поместить комментарий, сообщение, пользователя и поток в один и тот же пакет с именем blog. Если нам нужно добавить больше функций, таких как «лайки» или «рекомендации», мы можем добавить новый модуль/класс внутри пакета. Без проблем. Эта новая функция может иметь новые функции/методы для вызова извне и собственную часть документации.

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

Уважайте договор

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

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

Нет модуля «utils», уточните!

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

Это совершенно неправильно!

Если вы обнаружите, что для обработки даты недостаточно функций, вы можете создать свой собственный модуль «дата» и таким же образом с любыми другими функциями/методами.

Будьте осторожны с моками и заглушками

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

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

Точно так же никогда не имитируйте функции/методы тестируемого модуля/класса!

Не злоупотребляйте структурами try..catch

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

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