Рубрики
Без рубрики

#Learnbydiy – Как создать структуру тестирования блока JavaScript с нуля

Alcides Queiroz. # Learrnbydiy – Как создать JavaScript Блок тестирования Framework из царапин, так это как вывод нашей структуры тестирования будет выглядеть так, что это будет весело. =) Возможно, автоматизированные тесты являются частью вашей ежедневной рутины (если нет, перестаньте прочитать эту статью и начать

Автор оригинала: FreeCodeCamp Community Member.

Alcides Queiroz.

Я обещаю, это будет весело. =)

Возможно, автоматические тесты являются частью вашей ежедневной рутины (если нет, оставьте чтение этой статьи и Начните с самого начала , изучая от отца сама TDD). Вы использовали фреймворки тестирования, такие как Узел-нажмите ( или Лента ), Жасмин , Моча или Qunit До некоторое время просто принимая, что они делают некоторые волшебные вещи и не спрашивают о них слишком много вопросов. Или, если вы похожи на меня, может быть, вам всегда любопытно о том, как все работает, включая структуры тестирования, конечно.

Эта статья проведет вас через процесс создания структуры тестирования JavaScript с нуля, с довольно достойным DSL и красиво детализированным выходом. Это первая статья в моем #Learnbydiy серии. Идея состоит в том, чтобы демистифицировать определенные виды программного обеспечения, к которым мы привыкли, создавая более простые версии из них.

Отказ от ответственности

Перед началом некоторых важных заметок:

  • Цель этой статьи – не Создать готовый инструмент. Пожалуйста, Не используйте рамки, которые мы будем создавать, чтобы проверить производственный код. Его целью является чисто образовательным. =)
  • Естественно, наши маленькие рамки не будут полнофункциональными. Такие вещи, такие как Async-тесты, параллельные казни, более богатый набор списков, CLI (с параметрами, такими как --Watch ), подключаемые репортеры и DSLS и т. Д., Не присутствуют в нашей окончательной версии. Тем не менее, я настоятельно рекомендую что вы продолжаете играть с этим проектом и, возможно, попробуйте внедрить некоторые из этих недостающих частей Отказ Возможно, вы можете преобразовать его в серьезный проект с открытым исходным кодом. Я хотел бы знать, что этот проект игрушек стал «фактической» структурой тестирования.

⚔️. Тирион – крошечная структура тестирования

Наши рамки будут крошечными, но «смелыми» для его размера. Итак, нет лучшего имени, чем Тирион (Yeap, он тоже мой любимый получил характер).

Мы будем использовать Node.js в этом проекте, с хорошими и старыми модулями Commonjs. Минимальная версия узла, которой вам понадобится, это V8.6.0. Если у вас старая версия, пожалуйста, обновите ее.

О, я почти забыл … Я использую Пряжа На протяжении всей этой статьи, для таких вещей, как init init , пряжа ссылка И так далее, но вы можете использовать «Ваниль» NPM аналогичным образом ( NPM init , NPM Link , …).

Создание структуры папки проекта

Во-первых, давайте создадим следующую структуру папки:

tyrion/||______ proj/|      ||      |______ src/||______ playground/       |       |______ src/       |______ tests/

Другими словами”:

$ mkdir -p tyrion/proj/src tyrion/playground/src tyrion/playground/tests

Нам нужны две папки, каждая в отдельный проект.

  • ProJ Папка будет содержать пакет Tyrion Framework.
  • Детская площадка Папка будет содержать одноразовый проект узла для воспроизведения с нашими рамками. Он будет служить лабораторией во время нашего процесса развития.

Инициализация узловных проектов

Перейти к Детская площадка папка и запустить пряжа init -y Отказ Эта команда генерирует базовый Package.json файл. Откройте его, удалите "Главная": "index.js", Линия и добавьте запись «скрипты», как тот, который находится в примере ниже:

{  "name": "playground",  "version": "1.0.0",  "scripts": {    "test": "node tests"  },  "license": "MIT"}

После создания этого файла давайте сделаем то же самое для другого проекта, сам пакет Тириона. В ProJ Папка, запустить init init Отказ Это подскажет вам некоторую информацию для правильной создания файла Package.json. Введите следующие значения (выделенные жирным шрифтом):

question name (proj): tyrion question version (1.0.0): question description: question entry point (index.js): src/index.js question repository url: question author: question license (MIT): question private: 

Теперь нам нужно установить тирион в качестве зависимости разработки в нашей игровой площадке проекта. Если это был опубликованный пакет, нам просто нужно будет установить его напрямую, через NPM I --dev или пряжа Добавить --dev Отказ Как у нас только тирион локально, это невозможно. К счастью, как пряжа, так и NPM имеют функцию, чтобы помочь разработчикам во время этого пакета «Начало» фаза, позволяя нам моделировать связь между двумя пакетами (одна как зависимость другой).

Чтобы создать эту связь зависимости, перейдите к ProJ Папка и беги:

$ yarn link

Тогда в Папка для детской площадки , запустить:

$ yarn link tyrion 

Это все. Сейчас Тирион – это зависимость проекта игровой площадки.

Создание некоторых модулей, чтобы быть нашими «морскими свинками»

В Детская площадка/SRC Папка, давайте создадим два модуля, которые будут протестированы Tyrion:

Написание некоторых тестов

Сейчас самое время использовать наше воображение. Как выглядеть DSL Tyriion? Ты устал от ожидать , утверждать , и так далее? Давайте сделаем это другим, просто ради этого. Я предлагаю гарантия как наша функция утверждения. Вам нравится это?

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

И а Тесты/index.js Файл, чтобы импортировать наши тесты только в одном месте.

Тирион займет один из принципов узла:

Как вы можете помнить, в нашей игровой площадке Package.json файл, у нас есть Тест Сценарий, который просто работает Испытания узлов . Итак, чтобы выполнить его, просто введите NPM Test и нажмите ввод. Сделай это. Давайте посмотрим, сбой:

Эта ошибка ясна. У нас ничего нет в наших рамках. Модуль вообще не экспортируется. Исправить это, в ProJ Папка, создать SRC/index.js Файл экспортирует пустой объект, как вы можете увидеть ниже:

module.exports = {};

Теперь мы будем работать NPM Test опять таки:

Узел жалуется, потому что наш Гарантия Функция не существует. Это тоже просто исправить:

const guarantee = () => {};
module.exports = { guarantee };

Запустите тестовый скрипт снова:

Воила! Нет ошибок, но ничего не происходит, либо. =(

Функция гарантии

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

Давайте реализуем это:

И проверить, если он работает, давайте добавим еще одно утверждение к концу нашего Номер-utils.test.js файл:

guarantee(123 === 321); // This should fail 

Теперь запустите его еще раз:

Ага! Оно работает! Это уродливо, но это функционально.

Функция проверки

Нам нужен способ обернуть утверждения в тестовые агрегаты. По сути, все структуры тестирования имеют эту функцию, как Это функция в жасмине или Тест Функция в узле-кран.

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

Это то, что он будет выглядеть так:

Теперь мы можем переписать наши тесты, чтобы использовать новый Проверьте Функция:

И повторно запустить наш тестовый люкс:

Прохладный. Но … как насчет некоторых цветов ?? Разве это не было бы намного легче различить проходящие и неудачные тесты?

Добавьте Цвета Модуль как зависимость:

yarn add colors

Итак, импортируйте его на вершину PROJ/SRC/index.js файл:

const colors = require('colors');

И давайте поставим несколько цветов на нашем выходе:

const check = (title, cb) => {  try{    cb();    console.log(`${' OK '.bgGreen.black} ${title.green}`);  } catch(e) {    console.log(`${' FAIL '.bgRed.black} ${title.red}`);    console.log(e.stack.red);  }};

Так-то лучше. =)

Функция XCHECK

Было бы неплохо иметь простой способ отключить конкретный тест, например Xit функция в жасмине. Это может быть легко реализовано путем создания функции NO-OP, которая только что выводит, что тест отключен (ну, это не совсем полностью нет-OP, но почти):

const xcheck = (title, cb) => {  console.log(`${' DISABLED '.bgWhite.black} ${title.gray}`);};
module.exports = { guarantee, check, xcheck };

Итак, импортируйте xcheck Функция в Номер-utils.test.js Файл и отключить один из наших тестов:

const { guarantee, check, xcheck } = require('tyrion');const numberUtils = require('../src/number-utils');
// method: isPrimexcheck('returns true for prime numbers', () => {  guarantee(numberUtils.isPrime(2));  guarantee(numberUtils.isPrime(3));  guarantee(numberUtils.isPrime(5));  guarantee(numberUtils.isPrime(7));  guarantee(numberUtils.isPrime(23));});

И вот как это ведет себя:

Сводка теста и код выхода

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

Другая желаемая особенность – это резюме теста. Было бы неплохо знать, сколько пройденных тестов, не удалось или пропущено (отключенные). Для этого мы могли бы увеличить некоторые счетчики в обоих проверить и xcheck Функции.

Мы создадим конец Функция, которая печатает резюме теста и отделки с соответствующим кодом выхода:

И не забудьте назвать это в Детская площадка/тесты/index.js файл:

const { end } = require('tyrion');
require('./string-utils.test');require('./number-utils.test');
end();

Или, может быть:

const tyrion = require('tyrion');
require('./string-utils.test');require('./number-utils.test');
tyrion.end();

Теперь давайте повторно запустим NPM Test :

Отлично, это работает.

Функция группы

Многие тестовые рамки имеют какой-то способ связать связанные тесты. Например, в Жасмине есть Опишите функция. Мы осуществим Группа Функция для этой цели:

И обновите наши тесты, чтобы использовать эту новую функцию:

Вот новый выход:

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

Запустите его снова:

Это лучше!

Итак, как это работает?

  • повторять Функция повторяет строку N раз.
  • Отступ Функция повторяет отступ (из четырех пробелов) N раз, используя повторять функция.
  • Interntlines Функция Отсутствует строка с несколькими строками, добавив N отступлением к началу каждой строки. Мы используем его для отступа от стеков ошибки.
  • Indentlevel Переменная увеличивается в начале каждая групповая выполнение и уменьшается на ее конце. Таким образом, вложенные группы могут быть правильными отступами.

Больше соответствия

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

Во-первых, создайте Соответствует папка:

$ mkdir proj/src/matchers

Теперь мы создадим каждый партнер в отдельном файле:

то же самое Matcher использует строгий оператор равенства (===) для проверки, если два аргумента являются точно такими же объектом (для типов ссылок) или равны (для примитивных типов). Это ведет себя так же, как Тобе Matcher в Жасмине и T.equal в узле.

Примечание: Узел-нажатие также имеет соответствующий созычник t.same , но это работает по-другому (он не проверяет, если два объекта одинаковы, но если оба являются глубоко эквивалентными).

идентичны Matcher проверяет, что два аргумента эквивалентны. Он использует == Оператор для сравнения значений.

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

$ yarn add deep-equal

Потом:

Вот как ошибка будет выглядеть:

Falsy Matcher не удастся, если прилагаемое значение является правдой.

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

бросает Matcher пройдет, если функция бросает ошибку. Можно указать требуемое сообщение об ошибке, но это не обязательно.

index.js Файл для повторного экспорта всех сочетаний:

И, наконец, давайте приклеим их все вместе:

Вы можете использовать наши новые партнеры таким образом:

const { guarantee, check } = require('tyrion');
check('playing with our new matchers', () => {  // The original guarantee function still works  guarantee(123 === 123);
  guarantee.truthy('abc');  guarantee.falsy(null);
  const a = { whatever: 777 };  const b = a;  guarantee.same(a, b);  guarantee.identical(undefined, null);
  const c = { whatever: { foo: { bar: 'baz' } } };  const d = Object.assign({}, c);  guarantee.deeplyIdentical(c, d);
  function boom() { throw new Error('Some error...'); }  guarantee.throws(boom);  guarantee.throws(boom, 'Some error...');});

Функция Debresseach

Для реализации Rebedeach Функция, нам нужно использовать стек, чтобы накапливать все Rebedeach обратные вызовы. Это сделано для каждого нового выделенного уровня, создаваемого каждый раз, когда объявлена группа:

Как это работает?

  • Каждый раз, когда группа объявлена группа, мы нажимаем новый массив на foreachstack Переменная. Этот массив будет накапливаться все Rebedeach Обратные вызовы объявлены в этой области.
  • После завершения выполнения группы мы вынимаем массив в верхней части наших вызовов стека.
  • Rebedeach Функция получает обратный вызов и добавляет его в массив в верхней части стека наших обратных вызовов.
  • В начале каждого Проверьте Функция, мы называем каждый Rebedeach Обратный вызов на всех уровнях нашего стека.

Функция BeForalL

Наше последнее добавление будет BeForall функция. Ради простоты мы предполагаем, что звонки на BeForall Функция всегда будет поставлена перед всеми группами и тестами ( или , когда выделяются в группе, на самом вершине).

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

Наша версия BeForall просто получит обратный вызов и немедленно выполнить его.

const beforeAll = cb => cb();
module.exports = {   group, check, xcheck, guarantee, beforeAll, end };

Пример использования:

const { guarantee, check, group, beforeAll } = require('tyrion');
let a;beforeAll(() => {  a = { something: 'example' };});
group('playing with the beforeAll function', () => {  let b;  beforeAll(() => {    b = { something: 'example' };  });
  check('some test', () => {    guarantee.deeplyIdentical(a, b);  });
  check('another test', () => {    guarantee.identical(11, 11);  });});

Окончательная версия Тириона

Это было длительное путешествие, но Тирион наконец завершен. =)

Я добавил тихое вариант, который отключает журнал. Используется, чтобы облегчить тестирование Тириона (yep, Testing Frameworks тоже должны быть проверены).

Полный проект доступен здесь Отказ

Возможные улучшения

Тирион не хватает многих функций, таких как:

  • Поддержка Async Tests
  • Параллельное исполнение тестов
  • ДОПОЛЮЧЕНИЕ а также Послеобеда функции
  • А Xgroup Функция, которая отключает целую группу
  • Функция, похожая на Подгонка Жасмина
  • Шпион
  • Развязка DSL от логики отчетности.
  • Подключаемые репортеры
  • Терминал CLI (с помощью --watch опция)
  • Пока больше подписчиков
  • Слога ошибок дружелюбности

Я призываю вас продолжать играть с этим проектом. Не стесняйтесь использовать и расширить его. Пожалуйста, дайте мне знать ваши мысли, предложения и эксперименты, оставляя комментарий ниже. =)

Оригинал: “https://www.freecodecamp.org/news/learnbydiy-how-to-create-a-javascript-unit-testing-framework-from-scratch-c94e0ba1c57a/”