Добро пожаловать в последний выпуск из серии постов о популярном фреймворке AngularJS . Эта серия разработана, чтобы помочь вам избежать распространенных ловушек и ловушек, прежде чем они станут проблемой.
Лучшие пять ошибок, которые я вижу, люди делают:
- Сильная зависимость от $ scope (без использования контроллера как)
- Злоупотребляя $ watch
- Чрезмерное использование трансляции $ и $ emit
- Взлом DOM
- Неспособность проверить
Действительно ли неудача в тестировании — это проблема Angular или философии программирования в целом? Я думаю, что это немного, и в этом посте я объясню почему.
Неспособность проверить
Если вы не уверены в том, что тестирование связано с Angular, взгляните на их собственную статью руководства разработчика для модульного тестирования . Я просто процитирую следующее:
Angular написан с учетом тестируемости, но все равно требует, чтобы вы поступали правильно.
Это довольно четкая директива (некоторые из вас видят, что я там делал). Во-первых, я бы хотел узнать, как Angular использует тестирование. Затем я расскажу, почему это важно и как это не просто философское решение, когда вы создаете большие приложения Angular. Тогда я завершу с некоторыми заключительными мыслями. Если это работает для вас, продолжайте читать.
Угловой = Тестирование
Я довольно уверен в этом заявлении. Давайте рассмотрим факты:
- Каркас был написан для тестирования
- Внедрение зависимостей является большой частью структуры и помогает упростить модульное тестирование.
- Карма была написана специально для облегчения выполнения тестов
- Angular предоставляет модуль ngMock прямо из коробки, чтобы упростить тестирование, внедряя и высмеивая службы, такие как $ http
- Транспортир . Шутки в сторону.
Ну и что?
Некоторые из вас скажут: «И что?» То, что ты можешь что-то сделать, не означает, что ты должен , верно? К счастью, большинство моих решений в программировании прагматичны и основаны на реальном опыте, а не на гипотетическом понтификации. Я не только работал над проектами Angular, в которых не было тестов, или другими проектами, в которых были тесты, но я имел опыт участия в крупных проектах, которые начинались без тестов, а затем представил их, поэтому у меня есть довольно хорошее представление о влияние.
И все это позитивно.
Есть несколько очевидных преимуществ тестов, которые вы услышите, например:
- Находите дефекты как можно раньше (разве вы не слышали, что ошибки стоят дороже в процессе производства?)
- Рефакторинг помощи — разве вы не чувствуете себя лучше при изменении кода, когда знаете, что можете сразу запустить несколько тестов, чтобы выяснить побочные эффекты?
- Автоматическая регрессия — по мере развития вашего приложения тесты упрощают определение соответствия вашей кодовой базы изменениям
Однако для меня реальные преимущества тестирования не так очевидны, пока вы их не испытали.
Уточнение требований
Верьте или нет, хорошая стратегия тестирования может улучшить качество ваших требований. Это важно, когда вы управляете большим приложением. Фактически, один из моих любимых проектов включал совместную обратную связь между бизнесом, тестировщиками и программистами (вы знаете, это «гибкая вещь») и помог действительно уточнить требования к чему-то полезному.
Мы упростили это: при определении позиции невыполненных работ должны быть проверяемые критерии приемлемости. Мы работали с командой, чтобы гарантировать, что критерии действительно могут перейти в тест.
Итак, вы можете начать с чего-то вроде этого:
«Если пользователь вводит свою информацию, система должна рассчитать некоторые данные о пригодности».
Не очень хорошее требование, не так ли? Мы могли бы получить более конкретный и заявить,
«Учитывая пользователя, когда он вводит свою информацию, система должна вычислить базовую скорость метаболизма».
Это лучше, но я все еще не могу написать тест. Как насчет нескольких примеров? Вот тот, который является конкретным:
«Учитывая 40-летний мужчина 5 футов 10 дюймов, который весит 200 фунтов при подсчете BMR, то должен рассчитать 1929 год».
Вау! Теперь, когда я могу написать тест для. На самом деле, я написал один, который вы можете выполнить здесь, и он выглядит так:
describe("Formula for BMR", function () { describe("Given a 40-year old 5 ft 10 in male who weighs 200 pounds", function () { it("should compute a BMR of 1929", function () { var actual = formulaBmr({ isMale: true, height: 70, weight: 200, age: 40 }); expect(actual).toEqual(1929); }); }); });
Конечно, я добавил несколько других сценариев. Наличие требований с четкими критериями приемлемости, которые напрямую переводятся в тесты, полностью изменит способ доставки корпоративного программного обеспечения.
API дизайн
Второе влияние тестирования заключается в том, что оно напрямую влияет на дизайн вашего API. Я не говорю о запоздалых, постфактумных тестах, которые вы пишете, чтобы вы могли поставить галочку напротив надписи «Я написал тесты». Я говорю об истинно управляемой тестами разработке (TDD), которая включает в себя сначала написание теста, наблюдение за его неудачей, а затем написание простейшего фрагмента кода, который вы можете выполнить для теста. Вы должны попробовать это когда-нибудь. Сначала требуется много терпения, чтобы изменить подход к нему, но я обнаружил, что он создает чистый, читабельный, поддерживаемый код.
Например, в приведенном выше примере у меня изначально был интерфейс, который принимал несколько параметров для расчета. Я быстро понял, что не было видно, глядя на звонок, что передавалось или даже какой заказ. Поэтому я обновил API, чтобы вместо него взять объект параметра с четко определенными метками (задним числом я должен был быть еще более явным и сделал его heightInches и weightPounds).
Это привело к написанию сервиса, который выглядит следующим образом:
function formulaBmr(profile) { // Women - 655 + (4.35 x weight in pounds) + (4.7 x height in inches) - (4.7 x age in years) function woman(weight, height, age) { return Math.floor(655 + (4.35 * weight) + (4.7 * height) - (4.7 * age)); } // Men - 66 + (6.23 x weight in pounds) + (12.7 x height in inches) - (6.8 x age in years ) function man(weight, height, age) { return Math.floor(66 + (6.23 * weight) + (12.7 * height) - (6.8 * age)); } return profile.isMale ? man(profile.weight, profile.height, profile.age) : woman(profile.weight, profile.height, profile.age); }
Обратите внимание, что это отдельный, чистый объект JavaScript, который я могу использовать в любом приложении. Он становится «Angularized» только тогда, когда я регистрирую его с помощью внедрения зависимости:
(function (app) { app.factory('formulaBmrService', function () { return formulaBmr; }); })(angular.module('healthApp'));
Фактически, следование подходу, основанному на тестировании, почти всегда приводит к чистому, модульному, переносимому коду. Во многих случаях я обнаружил, что 80% кода не привязаны к конкретной структуре, а существуют как объекты домена, которые выполняют определенные задачи, и их втягивают в среду только для разрешения зависимостей и участия в привязке данных.
Дизайн программы
Тестирование не просто улучшает дизайн API. Когда я решил построить эмулятор 6502 , я не знал, с чего начать. Итак, я начал писать тесты, чтобы заставить процессор делать то, что я хотел . Процессор вряд ли бы много сделал без программного обеспечения для его запуска, и большинство примеров программ для 650-битного чипа 6502 написаны на языке ассемблера, который должен быть скомпилирован в машинный код. Чтобы это произошло, я написал набор спецификаций компилятора, чтобы я мог загружать программы и тестировать их в эмуляторе. В конце концов я запустил эмулятор. Чтобы увидеть его в действии, просто загрузите пример программы, скомпилируйте и запустите его.
Тесты могут помочь в разработке дизайна вашего приложения, и именно так я написал эмулятор.
Документация
Последнее не столь очевидное преимущество тестов заключается в том, что они документируют систему. Если вы не уверены, как работают «операционные коды» для чипа 6502, просто взгляните на тесты операционных кодов . Должно быть понятно, что обозначают различные метки, какова их функция и каков ожидаемый результат для различных операций. Даже конечные пользователи могут прочитать хорошо написанные спецификации тестов и понять, как система должна работать.
Это не останавливается там! Разработчики могут прочитать исходный код спецификаций, чтобы определить, как работают различные API. Пример, на который я ссылаюсь, демонстрирует различные флаги, которые существуют в процессоре 6502 и как они работают. Таким образом, тест может управлять проектированием на нескольких уровнях, предоставлять документацию о требованиях и точно демонстрировать, как использовать компоненты системы.
Разработка через тестирование
TDD — это подход, который требует дисциплины, терпения и, что важнее всего, участия, если ваша команда еще не приняла его. Даже если вы решите, что не хотите следовать подходу TDD, я настоятельно рекомендую вам сделать тестирование частью вашего жизненного цикла разработки. Не просто пишите тесты, чтобы поставить флажок или сделать номер покрытия кода, но вместо этого интегрируйте их как часть осмысленного подхода, чтобы пожинать преимущества, которые я перечислил.
Одна из техник, которые я нашел полезными для принятия тестов, заключается в двухстороннем подходе. Первый с требованиями. Убедитесь, что требования содержат критерии приемлемости, которые достаточно специфичны для написания тестов, и, если вы не пишете никаких других тестов, по крайней мере, напишите тесты, которые будут соответствовать критериям приемлемости.
Другой способ интегрировать тестирование — это цикл устранения дефектов Если в систему введена ошибка, первым шагом должно стать написание теста, который не может быть выполнен из-за ошибки. Вы не поверите, насколько эффективным может быть этот простой подход, если вы еще не следуете ему.
Во-первых, для того, чтобы дублировать ошибку, необходимо собрать достаточно информации для ее воспроизведения. Это возлагает ответственность на вашу команду по сбору необходимой вам информации. Это также требует глубоких знаний кода, чтобы вы знали, какие компоненты для написания теста вызывают сбой. Если вам трудно написать тесты для большинства ошибок, вам может потребоваться сделать шаг назад и оценить свою архитектуру. Та же конструкция, которая затрудняет тестирование, может способствовать нестабильности, которая в первую очередь вызывает появление дефектов.
Создание теста помогает вам лучше ознакомиться с кодом, связанным с дефектом, а также создает основу для предотвращения будущих дефектов. Как только у вас будет неудачный тест, вы можете изменить код так, чтобы тест прошел. После его прохождения вы можете с уверенностью передать исправление, к которому оно было адресовано, и медленно создать набор тестов, который гарантирует, что дефекты не появятся как побочные эффекты для последующих изменений.
Если вам нужен пример полного цикла разработки на основе тестирования для создания эталонного приложения, ознакомьтесь с моим постом под названием « Давайте создадим угловое приложение» ! Колода проведет вас через серию коммитов github, представляющих приложение, написанное с использованием подхода, основанного на тестировании. Вы можете увидеть, как он развивается, и посмотреть, как я предпринял простые шаги для объединения компонентов, что в конечном итоге способствовало достижению конечного результата .
Это все, что он написал!
Я сохранил этот пост напоследок, потому что считаю, что первых четырех ошибок на самом деле легче избежать, если вы используете тестовый подход. Практики Angular, которые я изучил за последние несколько лет, основаны на практическом опыте создания систем для масштабирования с большими группами и упрощения интеграции новых функций. Когда команда сосредоточена на подходе, основанном на тестировании, она стремится создавать решения, которые:
- Избегайте $ scope, когда это не нужно, потому что проще тестировать свойства и методы на простом старом объекте JavaScript (POJO)
- Не требуется накладных расходов на посторонние $ watch, потому что они спроектированы на основе состояний и переходов
- Связь между контроллерами независимо от иерархии или DOM
- Поддерживать состояние и логику в коде, который можно протестировать без присутствия браузера или DOM, поскольку он соединяется с помощью привязки данных и директив, а не явных манипуляций.
Как видите, топ-5 ошибок действительно все связаны! Я надеюсь, что эта серия помогла улучшить ваш подход к предоставлению бизнес-решений с Angular. Дайте мне знать ваши мысли и, пожалуйста, не стесняйтесь добавлять любые свои собственные советы или распространенные ошибки в комментариях ниже! Я с нетерпением жду вашего ответа и ценю всех вас, кто поделился вашими вопросами, мыслями, предложениями и советами в моих предыдущих постах в этой серии.