
Каждый разработчик программного обеспечения побывал в этом месте: их просят добавить разовую функцию, которая кажется безобидной, она раздувается на ряд взаимосвязанных функций, и, видя ее после многочисленных эволюций, желает, чтобы они не могли написать ее по-другому.
«Если бы мне сказали, зачем эта функция, я бы использовал фреймворк».
«Если бы я знал, насколько важна эта функция для моего клиента, я бы сделал ее более тестируемой».
Это борьба за итерацию, позволяющую органическому процессу создать конечный продукт вместо 100% продуманного дизайна, который ведет к желаемой цели.
Что я должен делать?
Когда разрабатывается небольшая функция, в первую очередь игнорируются тесты. Разработчики с разным опытом могут иметь возможность писать JavaScript, но хотят использовать те же проверяемые структуры, к которым они привыкли.
Внедрение зависимостей - это структура, которая позволяет коду быть чрезвычайно тестируемым, позволяя передавать зависимости чему-либо вне функции или объекта. Хотя в JavaScript есть свои соглашения относительно стиля кода, существует несколько передовых методов использования контейнеров внедрения зависимостей (DI) или инверсии управления (IOC) за пределами внешней среды.
Однако из-за гибкости JavaScript большинство шаблонов DI или IOC из других языков можно адаптировать к JS. Если вам не нужен сложный и структурированный шаблон, предлагаемый фреймворками, vanilla JS может использовать простые шаблоны, чтобы сделать код тестируемым.
Общеизвестно, что сложно проверить глобальные ценности. Код, управляющий DOM или имеющий дело с внешними библиотеками, загруженными с помощью тегов ‹script›, часто вводит глобальные значения. Если вы поддерживаете устаревший код или модифицируете код, чтобы он прослужил дольше, вы, вероятно, захотите, чтобы он был тестируемым.
Хорошая новость заключается в том, что код JavaScript можно легко написать или отредактировать, чтобы его можно было тестировать, не требуя расширенного фреймворка.
Примеры CodePen для всех описанных структур можно найти ниже. Для простоты во всех примерах используется одна и та же зависимость:
Закрытие
Замыкание - это основополагающая концепция JavaScript, которая дает одной функции доступ к области действия другой функции. Наличие функции, возвращающей функцию, дает вам возможность явно разрабатывать параметры, которые будут передаваться при начальной загрузке приложения, отдельно от вызова функции.
Проще говоря, вы можете передать зависимости один раз и вызвать функцию, как хотите, позже. Это убережет вас от крупных рефакторингов, которые требуют создания прототипов или реструктуризации зависимостей.
Если в базе кода уже используются концепции функционального программирования, это также поддерживает общий стиль. Это, вероятно, самый простой способ добавить тестируемости к устаревшему коду JavaScript.
Сеттер-прототип
Прототипы играют центральную роль в том, как JavaScript обрабатывает экземпляры объектов. В то время как большая часть внедрения зависимостей фокусируется на добавлении аргументов в конструктор класса, внедрение установщика-прототипа использует метод в экземпляре объекта для установки зависимости после создания.
Это позволяет задавать аргументы конструктора отдельно от зависимостей, предлагая четкое представление о том, что вводит зависимости, а что передает динамические значения.
Вы даже можете добавить что-то вроде метода getDependencies (), чтобы имитировать следующий пример, AngularJS Injectors. Он также позволяет иждивенцам устанавливать свойства своих зависимостей, как можно увидеть в методе setLogger () ниже.
Инжекторы AngularJS
Система внедрения AngularJS - один из самых простых способов для прототипа или функции определить, какие зависимости ему нужны. Затем эта абстракция может автоматически использоваться внешним инжектором.
Поскольку функции JavaScript также являются объектами, для них могут быть определены свойства. Если вы создаете свойство, которое представляет собой массив имен зависимостей в виде строк, соответствующих используемым аргументам конструктора, теперь у вас есть абстракция имени строки, определяющая зависимость.
Внешний загрузчик или инжектор может читать этот массив и передавать нужные аргументы по мере необходимости при вызове конструктора. Это сохраняет ответственность за создание экземпляров классов с контейнером IOC.
Проще говоря: функция может определять часть или все свои аргументы и позволять более крупному приложению решать, каковы эти зависимости.
Конструкторы контейнеров Symfony
Symfony - популярный объектно-ориентированный фреймворк для написания приложений на PHP. Многие фронтенд-разработчики познакомились с серверной разработкой через PHP, и многие фреймворки PHP основаны на Symfony и соответствующих контейнерах IOC.
В этом примере не создается контекст приложения, в котором хранятся контейнеры. Однако он позволяет обрабатывать зависимости как аргументами конструктора, так и методами установки.
Это особенно удобно, когда у вас есть зависимости от различных сторонних поставщиков, которые написали свои библиотеки по-разному.
Java-подобные бобы
Использование bean-компонентов - обычная практика в программировании на Java. Разработчики с опытом работы со Spring могут предпочесть этот тип централизованной конфигурации зависимостей.
Многие разработчики предпочитают, чтобы конфигурация была в статических файлах, таких как JSON или XML, и этот формат, напоминающий Beans, очень близок к этому.
Если используется сборщик, свойство proto каждой зависимости можно заменить строкой пути к файлу, и в этом случае конфигурация может быть действительно статической. В этом примере не использовался сборщик пакетов, чтобы показать, как этот шаблон можно использовать в устаревшем коде.
Вывод
Есть много способов сделать обычный код JavaScript более тестируемым.
В то время как JS может быть написан с использованием как функциональной, так и объектно-ориентированной парадигм, код, основанный на глобальных значениях, часто вносит недостаток тестируемости в базу кода, которую можно поддерживать.
Использование любого из этих методов для разделения зависимостей расширяет возможности тестирования стандартных сценариев JS. И когда придет время поменять зависимость, вы уже должны быть настроены для pivot.
Внимательные читатели заметят, что я делаю это в своих примерах, демонстрируя принцип подстановки Лискова в действии.
Надеюсь, что-то здесь окажется для вас полезным :)