Сервисы Digital Skynet
48

Разработка через тестирование: улучшаем навыки. Часть 2

В предыдущей статье мы рассмотрели теоретические аспекты. Самое время приступить к практике.

В закладки

Сделаем простую реализацию стека в JavaScript с помощью разработки через тестирование.

Стек — структура данных, организованная по принципу LIFO: Last In, First Out. В стеке есть три основные операции:

push: добавление элемента

pop: удаление элемента

peek: добавление головного элемента

Создадим класс и назовем Stack. Чтобы усложнить задачу, предположим, что стек имеет фиксированную вместимость. Вот свойства и функции реализации нашего стека:

items: элементы стека. Мы будем использовать массив для реализации стека.

capacity: вместимость стека.

isEmpty(): возвращает true, если стек пуст, иначе false.

isFull(): возвращает true, если стек достигает максимальной емкости, т.е. когда вы не можете добавить другой элемент. В противном случае возвращает false.

push(element): добавляет элемент. Возвращает Full, если стек переполнен.

pop: удаляет элемент. Возвращает Empty, если стек пуст.

peek(): добавляет головной элемент.

Мы собираемся создать два файла stack.js и stack.spec.js. Я использовал расширение .spec.js, потому что привык к нему, но вы можете использовать .test.js или дать ему другое имя и переместить в __tests__.

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

Сначала проверим конструктор. Чтобы протестировать файл, вам необходимо импортировать файл стека:

Для тех, кому интересно, почему я не использовал здесь import— последняя стабильная версия Node.js не поддерживает эту функцию на сегодняшний день. Я мог бы добавить Babel, но не хочу перегружать вас. Когда вы тестируете класс или функцию, запустите тест и опишите, какой файл или класс вы тестируете. Здесь речь идет о стеке:

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

Заметьте, что мы используем toEqual и не используем toBe для stack.items, потому что они не ссылаются на один и тот же массив.Теперь запустим yarn test stack.spec.js. Мы запускаем Jest в определенном файле, потому что мы не хотим, чтобы другие тесты были испорчены. Вот результат:

Stack is not a constructor. Конечно. Мы до сих пор не создали наш стек и не сделали constructor. В stack.js создайте свой класс, конструктор и экспортируйте класс:

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

Поскольку мы не устанавливали элементы в конструкторе, Jest ожидал, что элементы в массиве будут равны [], но они не определены. Затем вы должны инициализировать элементы:

Если вы снова запустите тест, вы получите такую же ошибку для capacity, поэтому вам также нужно будет установить вместимость:

Запустим тест: Да! Тест пройден. Вот что такое TDD. Надеюсь, теперь тестирование будет иметь для вас больше смысла! Продолжим?

isEmpty

Чтобы проверить isEmpty, мы собираемся инициализировать пустой стек, протестировать, если isEmpty возвращает true, добавить элемент и снова проверить его.

Когда вы запустите тест, вы должны получить следующую ошибку:

Чтобы решить эту проблему, нам надо создать isEmpty внутри класса Stack:

Если вы запустите тест, вы должны получить еще одну ошибку:

В isEmpty ничего не добавляется. Stack пуст, если в нем нет элементов:

isFull

Это то же самое, что и isEmpty, так как это упражнение проверяет эту функцию с помощью TDD. Вы найдете решение в самом конце статьи.

Push

Здесь нам надо протестироватьтривещи:

  • новый элемент должен быть добавлен в начало стека;
  • push возвращает «Full», если стек заполнен;
  • элемент, который был недавно добавлен, должен быть возвращен.

Создадим другой блок, используя describe для push. Вкладываем этот блок внутри основного.

Добавление элемента

Создадим новый стек и добавим элемент. Последним элементом массива items должен быть только что добавленный элемент.

Если вы запустите тест, то увидите, что push не определен и это нормально. push потребуется параметр, чтобы что-то добавить в стек:

Тест снова пройден. Вы что-то заметили? Мы сохраняем копию этой строки:

Это очень раздражает. К счастью, у нас есть метод beforeEach, который позволяет выполнять некоторую настройку перед каждым тестовым прогоном. Почему бы не воспользоваться этим?

Важно:

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

Важнее важного:

мы также должны создать метод afterEach. Экземпляр стека теперь будет использоваться для всех тестов. Это может вызвать некоторые трудности. Сразу после beforeEach добавьте этот метод:

Теперь вы можете удалить инициализацию стека во всех тестах. Вот полный тестовый файл:

Тестирование возвращаемого значения

Есть тест:

Когда вы запустите тест, получите:

Ничего не возвращается внутри push! Мы должны исправить это:

Возвращение full, если стек переполнен

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

Вы увидите эту ошибку, когда запустите тест:

Итак, элемент добавлен. Это то, что мы и хотели. Сначала нужно проверить, заполнен ли стек, прежде чем добавить что-то:

Тест пройден.

Упражнение: pop и peek

Самое время попрактиковаться. Протестируйте и реализуйте pop и peek.

Подсказки:

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

Не смотрите на решение ниже, не попытавшись сделать самостоятельно. Единственный способ прогресса — пытаться, экспериментировать и практиковаться.

Решение

Ну как упражнение? Справились? Если нет, не расстраивайтесь. Тестирование требует времени и усилий.

Если вы посмотрите на файлы, вы увидите, что я использовал тройное условие в pop и peek. Это то, что я рефакторил. Старая реализация выглядела так:

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

Запустим тест еще раз:

Тесты улучшают качество кода. Надеюсь, вы теперь понимаете всю пользу тестирования и будете чаще применять TDD.

Перевод статьи How to get better at testing with test-driven development от Digital Skynet :)

Материал опубликован пользователем. Нажмите кнопку «Написать», чтобы поделиться мнением или рассказать о своём проекте.

Написать
{ "author_name": "Digital Skynet", "author_type": "self", "tags": [], "comments": 1, "likes": -3, "favorites": 0, "is_advertisement": false, "subsite_label": "services", "id": 51879, "is_wide": false, "is_ugc": true, "date": "Tue, 27 Nov 2018 13:20:14 +0300" }
{ "id": 51879, "author_id": 187179, "diff_limit": 1000, "urls": {"diff":"\/comments\/51879\/get","add":"\/comments\/51879\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/51879"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 200396, "possessions": [] }

1 комментарий 1 комм.

Популярные

По порядку

1

Хабровчане потихоньку переезжают на Виси?

Ответить
0
{ "page_type": "article" }

Прямой эфир

[ { "id": 1, "label": "100%×150_Branding_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox_method": "createAdaptive", "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfl" } } }, { "id": 2, "label": "1200х400", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfn" } } }, { "id": 3, "label": "240х200 _ТГБ_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fizc" } } }, { "id": 4, "label": "240х200_mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "flbq" } } }, { "id": 5, "label": "300x500_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfk" } } }, { "id": 6, "label": "1180х250_Interpool_баннер над комментариями_Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "ffyh" } } }, { "id": 7, "label": "Article Footer 100%_desktop_mobile", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjxb" } } }, { "id": 8, "label": "Fullscreen Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjoh" } } }, { "id": 9, "label": "Fullscreen Mobile", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjog" } } }, { "id": 10, "disable": true, "label": "Native Partner Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyb" } } }, { "id": 11, "disable": true, "label": "Native Partner Mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyc" } } }, { "id": 12, "label": "Кнопка в шапке", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "bscsh", "p2": "fdhx" } } }, { "id": 13, "label": "DM InPage Video PartnerCode", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox_method": "createAdaptive", "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "flvn" } } }, { "id": 14, "label": "Yandex context video banner", "provider": "yandex", "yandex": { "block_id": "VI-223676-0", "render_to": "inpage_VI-223676-0-1104503429", "adfox_url": "//ads.adfox.ru/228129/getCode?pp=h&ps=bugf&p2=fpjw&puid1=&puid2=&puid3=&puid4=&puid8=&puid9=&puid10=&puid21=&puid22=&puid31=&puid32=&puid33=&fmt=1&dl={REFERER}&pr=" } }, { "id": 15, "label": "Плашка на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byudx", "p2": "ftjf" } } }, { "id": 16, "label": "Кнопка в шапке мобайл", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byzqf", "p2": "ftwx" } } }, { "id": 17, "label": "Stratum Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvb" } } }, { "id": 18, "label": "Stratum Mobile", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvc" } } }, { "id": 19, "label": "Тизер на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "p1": "cbltd", "p2": "gazs" } } } ]
Компания отказалась от email
в пользу общения при помощи мемов
Подписаться на push-уведомления
{ "page_type": "default" }