CodeceptJS — современные end2end тесты для NodeJS

    image


    В мире NodeJS творится полный хаос, каждый день появляются новые фреймворки, размеры зависимостей обычного package.json вырастают на сотни мегабайт, а библиотека, которую вы добавили в проект ещё вчера, сегодня уже морально устарела. И если в мире фронтенд фреймворков уже наметились явные фавориты: AngularJS, React, Vue, Ember, то что для приемочного тестирования совершенно непонятно. Каждый фреймворк предоставляет свой синтаксис и свои крутые фичи, а также у каждого есть свой характерный набор проблем. Например, каждый по-своему реализует взаимодействие с браузером, каждый по разному борется с асинхронностью.


    Ох, эта пресловутая асинхронность...


    Да, асинхронность это круто, но в контексте приемочного end-2-end тестирования асинхронность это вечная проблема. Тесты должны иметь линейный вид, посоледовательности команд: я открываю страницу, я кликаю кнопку, я хочу увидеть нужный мне текст. Каждая подобная команда в контексте JS будет выполнятся асинхронно, а потому подобная конструкция будет записана цепочкой вызовов:


    client
      .url('https://github.com/nightwatchjs/nightwatch')
      .waitForElementVisible('body', 1000)
      .assert.visible('.container h1 strong a')

    (все команды смешаны в одну, сложно понять какие команды доступны)


    или с помощью многократных yield или await:


    function*() {  
      yield browser.url('http://google.com');
      yield browser.click('#link');
      var title = yield browser.getTitle()
      console.log(title);
    }

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


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


    Для решения описанных выше проблем (мультиинструментальность и асинхронность) появился CodeceptJS.


    Это фреймворк позволяющий описывать тесты на DSL высокого уровня. Сами тесты будут выполнятся одной из популярных библиотек на выбор: webdriverio, Protractor, NightmareJS. В большинстве случаев вам не придется учить синтаксис каждой из библиотек, а использовать готовый универсальный API. Вот как выглядит простой тест в CodeceptJS:


    Scenario('search github', (I) => {
      I.amOnPage('https://github.com/search');
      I.fillField('Search GitHub', 'CodeceptJS');
      I.pressKey('Enter');
      I.see('Codeception/CodeceptJS', 'a');
    });
    
    Scenario('register', (I) => {
      I.amOnPage('https://github.com');
      within('.js-signup-form', function () {
        I.fillField('user[login]', 'User');
        I.fillField('user[email]', 'user@user.com');
        I.fillField('user[password]', 'user@user.com');
        I.click('button');
      });
      I.see('There were problems creating your account.');
    });

    Как же CodeceptJS позволяет писать линейные сценарии для тестов? Раскрою секрет: везде используется глобальная цепочка промисов, в которую добавляются новые и новые команды. Таким образом, CodeceptJS может легко реализовать те же PageObjects:


    Scenario('register', (I, RegisterPage) => {
      RegisterPage.open();
      RegisterPage.register({login: 'User', email: 'user@user.com', password: '123435'});
      I.see('There were problems creating your account.');
      I.click('Explore');
    });

    Таким образом мы перенесли команды из предыдущего примера в отдельно созданный класс.


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


    Scenario('пробую написать реферат', (Я) => {
      Я.на_странице('http://yandex.ru/referats');
      Я.вижу("Написать реферат по");
      Я.выбираю_опцию('Психологии');
      Я.кликаю("Написать реферат");
      Я.вижу("Реферат по психологии");
    });

    Да, это валидный JS-код, который может быть выполнен в среде NodeJS без всяких транспайлеров!


    Как уже было упомянуто в конфигурации вы легко сможете выбрать движок для вполнения тестов. Хотите стабильный и хороший webdriver API, используйте WebDriverIO, работаете с Ангуляром — включите Protractor, хотите ускорить тесты в 3 раза, отказавшись от webdriver, и запуская приложение внутри Electron, используйте Nightmare.


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


    Что ещё есть в CodeceptJS:


    • пошаговый вывод прохождения тестов
    • режим интерактивной паузы
    • генераторы тестов, pageobjects, helpers
    • скоро появится поддержка Appium

    Как установить CodeceptJS? Не так и сложно


    [sudo] npm install -g codeceptjs
    codeceptjs init

    Дальше вам нужно будет выбрать один из драйверов для вполнения тестов (Protractor, webdriverio, Nightmare) и установить для них пакеты. Команда codeceptjs init поможет вам в этом и подскажет как писать первый тест.


    Проекту уже почти год, CodeceptJS используется в Британских и в Бразильских государственных компаниях. Он бесплатен и свободен как MIT.
    Если вы ещё не пробовалb его, у вас есть отличная возможность! Если уже пользуетесь — пишите свои комментарии. А также присоединяйтесь к развитию проекта.

    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 21
    • –5
      Какая-то ядреная смесь кукумбера и жасмина. И еще надстройка над нестабильным протрактором, а каждый слой абстракции добавляет своих багов…
      • +7

        Узнаю старый добрый хабр. Сразу скепсис и мокание в дерьмо. Как же я по этому соскучился :)
        Согласен с претензией насчет абстракции. Есть такая проблема. Но как мне кажется проблема написания и поддержки тестов на "чистом" SeleniumWebDriverJS (или его аналогах) стоит гораздо острее. Опять таки, легко запутаться в промисах или слишком углубиться в техническую имплементацию потеряв суть.

        • +1
          Опять таки, легко запутаться в промисах или слишком углубиться в техническую имплементацию потеряв суть.

          Несогласен, т.к. это зависит от архитектуры приложения и качества кода.
          • +1

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

      • +2
        Казалось бы, что можно сделать, чтобы тест ещё более читабельным. Давайте напишем его по-русски!

        Не прошло и двух месяцев с момента последней попытки перевести JS на русский, как вот еще одна

        • +2

          Шутки-шутками, но фича с переводами появилась после пул реквеста одного разработчика из Сан Пауло, который разрабатывал правительственную систему и соответственно хотел писать тесты на португальском. Я в португальском ничего не понял, потому добавил перевод на русский. Кому надо — тот воспользуется, кому не надо — посмеется.

        • –4
          И если в мире фронтенд фреймворков уже наметились явные фавориты: AngularJS, React, Vue, Ember

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

          • +10

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

            • –1

              У меня появился бы такой вопрос, если бы речь шла о просто библиотеках, а не о фреймворках. А у вас?

              • +4

                Мне кажется спор о том что какой на кого ярлычок навесить совершенно неуместный. Реакт сам по себе библиотека, но вместе со всей своей экосистемой занимает ту же нишу, что и Ангуляр, Эмбер и Vue. Потому вполне логично разместить их в одном ряду.

            • +3

              Какая, блин, разница в контексте этой статьи?

              • –1
                Религиозная. Любители React очень трепетно относятся к тому, что их любимый инструмент — библиотека, более легковестная, простая в изучении… Правда умалчивают обычно о том, сколько еще библиотек надо поставить для того, чтобы получить нужный результат и то, сколько потом это будет весить… Поэтому я согласен с автором статьи — когда мы говорим React, то имеем ввиду его экосистему. А она уже — фреймворк
                • –1
                  Экосистему React'а сложно назвать фреймворком. Фреймворк диктует архитектуру, а приняв решение использовать React в проекте я не получаю сколь-нибудь готовой архитектуры, мне надо над ней думать и создавать свою или искать какие-то готовые решения в этой экосистеме. Из «коробки» есть только рендеринг компонентов с примитивной системой хранения состояний, которую в подавляющем большинстве проектов если и используют, то только для каких-то очень локальных состояний, а так берут что-то типа Redux или MobX для состояний. Которые опять же библиотеки, для которых есть «биндинги» к React. В общем для каждого проекта нужно самому создавать свой фремйворк. Или взять какой-нибудь из популярных, которых не менее десятка.
                  • 0
                    Вот — вы сами сказали, что мы собираем свой фреймворк. По сути, используя React каждый делает сборку фреймворка. Даже если это самописное решение для React — это все равно часть созданной программистом/командой экосистемы, т.е. мы получаем фреймворк.

                    Что есть фреймворк? Это структура + более высокоуровневая абстракция. React+Redux+смасса библиотек, реализующих нужный функционал это дают. Чем в финальной «сборке» всех нужных составляющих это отличается от какого-нибудь Ember? ИМХО только тем, что составляющие ты выбрал сам.

                    Достоинства React — ты можешь «выбрать компоненты» для своего «идаельного» фреймворка. Минус — нет четких парадигм. Два проекта на React могут сильно отличаться. Хотя в сухом итоге мы получим всё тот же фреймворк, способный делать ровно столько же, сколько Ember/Angular/etc., но с необходимостью отслеживать зависимости и удостоверяться, что Казуал Карл не забил на поддержку конкретной библиотеки, которая есть часть проекта.
                    • 0
                      Что есть фреймворк? Это структура + более высокоуровневая абстракция.

                      Уточнение: структура, которую сложно игнорировать + более высокоуровневая абстракция над инфраструктурой, которую (абстракцию) обычно нет смысла игнорировать + (опционально) набор готовых компонентов для задач типа валидации, которые часто можно игнорировать. Собственно хороший фреймворк отличается от плохого двумя вещами, по-моему: сколько инфраструктурного и прочего не относящегося к конкретной задаче кода придётся писать для её решения и насколько легко заменить какой-то компонент фреймворка сторонним или самописным, если встроенный чем-то не устраивает, не теряя поддержки по остальным компонентам.


                      Минус — нет четких парадигм.

                      Это сомнительный минус. Скажем, в сейчас активно разрабатываемых приложениях мы отказались от "стандартного" однонаправленного потока данных с единым хранилищем состояния на плоских JS-объектов на базе Flux/Redux, сочтя его оверинжинирингом для наших задач. Если бы Redux был неотъемлемой частью React, то, вероятно, стали бы смотреть в сторону чего-то другого.


                      способный делать ровно столько же, сколько Ember/Angular/etc.

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

                      • 0
                        сколько инфраструктурного и прочего не относящегося к конкретной задаче кода придётся писать для её решения и насколько легко заменить какой-то компонент фреймворка сторонним или самописным, если встроенный чем-то не устраивает, не теряя поддержки по остальным компонентам.

                        Вот тут не согласен — чем более низкоуровневое решение, тем проще обойти фреймворк. Пока вы будете в парадигме какого-нибудь Ember мыслить — обойти его изкаробочные части сложно, как только вы отказываетесь от Ember Object/Data и начинаете просто писать решения для ES6 — все становится просто.
                        Это сомнительный минус. Скажем, в сейчас активно разрабатываемых приложениях мы отказались от «стандартного» однонаправленного потока данных с единым хранилищем состояния на плоских JS-объектов на базе Flux/Redux, сочтя его оверинжинирингом для наших задач. Если бы Redux был неотъемлемой частью React, то, вероятно, стали бы смотреть в сторону чего-то другого.

                        Всё просто — если я знаю Ember, то влиться в любобй проект на нём будет легко. Если я знаю React — то не факт, вы сами привели пример. А для бизнеса интересны решения, которые потом будет в свостоянии кто-то поддерживать.
                        Не сочитите за троллинг, вопрос к React-специалисту — почему вы не воспользовались еще до React каким-нибудь handlebars или чем-то подобным? Что реально отличает React от остальных настолько? Я просто не понимаю хайпа вокруг него…
                        Обычно не столько же, а значительно меньше. При использовании «монолитных» фреймворках часто вся их функциональность не востребована, и даже если умный сборщик в конечный билд её включать не будет, то всё равно во время разработки она будет где-то рядом (например в node_modules) и хорошо если не будет ограничивать свободу своим наличием.

                        На счет нахождения рядом — согласен, но вам не все ли равно? Я получал из Ember сборку равную проекту на React (последний даже был тяжелее на пару десятков кб) — сложностей не возникло. А про ограничения — как? Я такого еще не видел ни разу.
                        • 0
                          Что реально отличает React от остальных настолько?

                          1. React предоставляет полноценный механизм View (в терминах MVC) и даже ViewModel (setState), хотя часто его не используют, предпочитая Redux, MobX или что-то другое, в том числе своё, обеспечивая автоматический ререндеринг при изменении свойств. Handlebars и прочие — лишь шаблонизаторы, не решающие когда нужно произвести повторный рендеринг.


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

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

            • +4
              Мне не хватает в вашей статье обработки ошибок. Как это делается и какие есть возможности. Особенно синтакс, где именно и что завалилось. А то когда все хорошо у всех все хорошо :) Однако во время разработки, все хорошо с первого раза бывает редко.
              • +2

                Спасибо, действительно важный момент.


                1) описание ошибок:


                -- FAILURES:
                
                  1) Testing Begins: ANI testing:
                
                      expected web page to include "bogus text that is not there"
                      + expected - actual
                
                      -Home
                      -About
                      -Portfolio
                      -Services
                      -My Account
                      -Contact
                      -bring your site to life
                      -content rich
                      -graphically interesting
                      -search engine optimized
                      ---( 49 lines more )---
                      +bogus text that is not there
                
                  Scenario Steps:
                
                  - I.see("bogus text that is not there") at Test.<anonymous> (examples/absolutenet_test.js:8:5)
                  - I.grabTitle() at Test.<anonymous> (examples/absolutenet_test.js:6:23)
                  - I.amOnPage("http://www.absolutenet.com/") at Test.<anonymous> (examples/absolutenet_test.js:5:5)
                
                  Run with --verbose flag to see NodeJS stacktrace
                
                  2) GitHub: register:
                     Field q not found by name|text|CSS|XPath
                
                  Scenario Steps:
                
                  - Within .js-signup-form: I.click("button") at examples/github_test.js:31:7
                  - Within .js-signup-form: I.fillField("q", "aaa") at examples/github_test.js:30:7
                  - Within .js-signup-form: I.fillField("user[password]", "user@user.com") at examples/github_test.js:29:7
                  - Within .js-signup-form: I.fillField("user[email]", "user@user.com") at examples/github_test.js:28:7
                  - Within .js-signup-form: I.fillField("user[login]", "User") at examples/github_test.js:27:7
                  - I.seeInCurrentUrl("/explore") at Test.<anonymous> (examples/github_test.js:35:5)
                  - I.click("Explore") at Test.<anonymous> (examples/github_test.js:34:5)
                  - I.see("There were problems creating your account.") at Test.<anonymous> (examples/github_test.js:33:5)
                

                2) при запуске с флагом verbose покажет полный стектрейс + все состояния глобального промиса, чтобы можно было проследить какие события действительно выполнились, а какие — нет. Помогает разбирать кейсы, когда случайно забыл вернуть промис и он потерялся.


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

            • +1

              По опыту в нашей компании — нам оказалось достаточно просто обучить QA отдел CodeceptJS. Для не программистов, поверхностно знающих JS, порог воходения на приемлемом уровне. Ребята пробовали разные языки и фреймворки, для сравнения. Остановились на CodeceptJS. Разумеется пользуются и другими инструментами. Но CodeceptJS делает свое дело — решает те задачи, которое перед ним ставят. В целом, все что могу сказать — Спасибо :)

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