Модульное тестирование в гибких веб-проектах

Почему это важно и с чего начать

Если вы фронтенд-разработчик, то, вероятно, слышали о модульном тестировании и что это хорошо. Опрос Состояние фронтенд-инструментов в 2016 году показывает, что 52% всех разработчиков проводят модульное тестирование своего кода - на 12% больше, чем годом ранее.

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

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

Что такое модульные тесты и как они работают?

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

Модульный тест - это функция, которая выполняется программным обеспечением, называемым test-runner. В этой функции вы предоставляете четыре вещи:

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

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

Реальный тест может выглядеть так:

describe('hasSearchedBefore', () => {
  it('should return true when search status is "success"', () => {
    const state = { searchStatus: status.success };
    expect(hasSearchedBefore(state)).to.be.true;
  });
});

Здесь мы тестируем функцию под названием «hasSearchedBefore», которая принимает объект состояния в качестве аргумента и возвращает логическое значение. Мы определяем нашу тестовую функцию как параметр функции с именем «it», которая предоставляется нашим средством выполнения тестов. Функция «expect» является частью нашей библиотеки утверждений. Библиотека утверждений предоставляет удобные методы для простого сравнения нашего фактического результата (hasSearchedBefore (state)) с нашим ожидаемым результатом («true»).

Как правило, многие из этих тестов, которые имеют схожую цель или тестируют одну и ту же сущность, сгруппированы вместе в набор тестов (в нашем примере функция «описать»). Что касается функции «hasSearchedBefore», мы, вероятно, захотим дополнительно протестировать функцию с состоянием, которое будет выдавать ложное возвращаемое значение, а также без каких-либо параметров.

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

Зачем мне писать модульные тесты?

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

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

Что нужно тестировать?

  • Многоразовые компоненты
  • Библиотеки, ПО с открытым исходным кодом в целом
  • Вызовы API и обработка ответов в вашем приложении
  • Обработка ошибок
  • Приложения и бизнес-логика
  • Взгляды
  • Регрессионные тесты на ошибки

Как начать работу с модульным тестированием?

Шаг 1. Напишите тестируемый код

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

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

В лучшем случае ваш код является модульным на файловой основе, так что каждый файл с кодом JavaScript может сопровождаться соответствующим тестовым файлом. Предполагая, что у нас есть файл с именем personModel.js в нашем проекте, мы бы поместили другой файл с именем personModel.spec.js прямо рядом с ним, который содержит все модульные тесты для человека. модель.

Шаг 2. Настройте инструменты для тестирования вашего кода

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

  • Node.js в качестве тестовой среды (вы также можете запускать тесты в браузере, но это сложнее автоматизировать)
  • Мокко как тестовый раннер
  • Чай как библиотека утверждений

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

  • JSDOM для проверки взаимодействия с DOM (поскольку Node.js не имеет реализации DOM)
  • Энзим для удобного тестирования компонентов React
  • Sinon для тестирования вызовов функций и имитации асинхронного поведения и вызовов Ajax.

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

Наш шаблон внешнего интерфейса от Aperto Move с вышеупомянутым тестовым стеком можно найти на GitHub:



При использовании нашего шаблона каждый файл с окончанием * .spec.js автоматически распознается как файл, содержащий модульный тест, объединенный со всеми другими файлами модульного теста и запускаемый Mocha.

В свой тестовый файл вы должны импортировать необходимые тестовые библиотеки и код, который хотите протестировать, с помощью импорта ES6, затем написать свои тесты и выполнить тестовую задачу («npm test») в командной строке. .

Бонус: если вы используете среду непрерывной интеграции, такую ​​как Jenkins, Travis CI, Bamboo и т. д., вы можете автоматически выполнить эту задачу как часть этапа сборки. Таким образом, вы можете гарантировать, что на ваш действующий сервер будет развернут только код, который проходит все модульные тесты.

Когда я должен тестировать? До или после внедрения?

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

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

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

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

Как тестирование вписывается в процесс гибкой разработки?

Чтобы гарантировать, что написание тестов не пропущено, мы создаем обязательную подзадачу под названием «написание модульных тестов» для каждой пользовательской истории в начале каждого спринта. Эта задача обычно завершается после внутреннего анализа UX- и UI соответствующей истории, который проводится сразу после реализации.

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

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

Стоит ли в целом усилий?

Это. Время, необходимое для написания тестов, будет зависеть от нескольких других этапов процесса разработки:

  • Специалист по контролю качества может тестировать быстрее и должен писать меньше отчетов об ошибках.
  • Меньше времени тратится на исправление ошибок, что позволяет быстрее внедрять новые функции
  • Другим разработчикам не нужно бояться поломать что-то, и они могут быстрее погрузиться в проект.
  • Если ошибки действительно возникают, их можно отследить и локализовать за меньшее время.
  • Регрессии устраняются, если тест написан для каждой обнаруженной ошибки.

Когда я могу пропустить модульные тесты?

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

Что еще я могу сделать?

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

Если совместимость браузера является важным фактором в вашем проекте, вы можете автоматически запускать модульные тесты в разных браузерах на реальных устройствах либо на своей собственной ферме устройств (например, с помощью Karma), либо с помощью облачной службы. такие как Sauce Labs.

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

Последним шагом в автоматизации тестирования являются сквозные тесты. Эти тесты охватывают определенные процессы, которые пользователи выполняют с вашим приложением в целом, включая бизнес-логику, интерфейсные представления и внутреннее взаимодействие. В сквозных тестах серия взаимодействий с пользователем моделируется с помощью сценария, который управляет экземпляром вашего приложения в браузере и проверяет, отвечает ли ваше приложение на ввод, как ожидалось. Примером может быть автоматическое заполнение формы входа с недопустимыми данными входа и их отправка, а затем программная проверка, появляется ли соответствующее сообщение об ошибке на веб-сайте. Хорошим решением Node.js для сквозного тестирования, которое мы используем, является nightwatch.js.

Как мне начать сейчас?

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

Вначале это может занять некоторое время, но очень скоро вы станете быстрее. Кроме того, ваши первые тесты не будут иметь самого элегантного кода, но это нормально. Простой тест с неоптимальной производительностью лучше, чем отсутствие теста. Кроме того, вы очень быстро наберетесь опыта. Поначалу модульное тестирование может показаться немного пугающим, но это не ракетостроение;)

Дальнейшее чтение













Aperto Move - компания IBM - берлинское агентство цифровых и мобильных услуг, в котором работает около 40 сотрудников. Как насчет вас ?

Следуйте за нами: apertomove.com / Facebook / Twitter