Pull to refresh

Unit тестирование в js. YATS — поделка для написания юнит тестов

Reading time6 min
Views6.2K
Добрый день!

Начать свою статью я хочу с небольшого вступления. Вы помните, какими были сайты лет 10 назад? а пять? Если сайты и содержали какую-то js логику, то она была проста и наивна. На сегодняшний день каждый второй — это не просто статические данные, это большое динамическое содержимое, с «кучей» js логики.

За 5-8 лет JavaScript перестал быть языком для анимирования снежинок под новый год и преобразовался в довольно популярный и востребованный язык программирования, с большим коммьюнити.

Любой код можно сделать лучше, если покрыть его тестами. Код, покрытый тестами проще рефакторить, при написании tests first можно писать удобный расширяемый код.

В таких задачах хорошо помогает UNIT-тестирование.

На сегодняшний день существует множество фреймворков для unit тестирования js кода. В данной статье я бы хотел описать свое видение небольшой библиотеки для тестирования js кода.


Зачем хотим



Создать небольшую библиотеку, содержащая необходимые методы для работы с кодом и предоставляющая удобный интерфейс для работы как на клиентской стороне, так и с node.js

Что хотим



  • Поддержка цепочных вызовов для возможности писать код без постоянного «упоминания» объекта для тестирования
  • Не засорять кучей глобальных переменных пространство (при подключении напрямую)
  • Наличие как общих методов для тестирования, так и специфичных, но подчас необходимых (рекурсивное сравнение объектов и т.п.)
  • Поддержка подключения как «нативно» (через тег script), так и с помощью require.js (либо commonJS)
  • Возможность получить результаты тестов как консольно (клиентская сторона\node.js), так и в виде html (мобильные браузеры)
  • Возможность выбрать html ноду в качестве наблюдаемой и очищать после каждой группы тестов (удобно для тестирования, например, виджетов)
  • Возможность группировать тесты и получать сложные деревья тестов, например:
    Тесты модуля N
    |
    |__Тесты подмодуля N:X
    | |
    | |__Тесты функции Z подмодуля X
    |
    |___Тесты функции Y модуля N


Что получилось



Чтобы долго не томить рассказами — библиотека находится здесь: github.com/xnimorz/YATS (здесь же и небольшое количество примеров)
wiki страница — xnim.ru/blog?id=38 (здесь ждет более подробное описание возможностей библиотеки с примерами)
Посмотреть работу библиотеки можно здесь: xnimorz.github.io/YATS/EXAMPLE.html
Пример кода для тестирования — браузеры ( github.com/xnimorz/YATS/blob/master/example.js ) — здесь содержится пример работы всех методов бибилиотеки
node.js — github.com/xnimorz/YATS/blob/master/nodeExample.js

Что умеем



Библиотека создает глобальный объект yats (если подключать не через require).
Основными методами библиотеки являются методы:
  • ok — проверяет выражение на истинность (принимает любые объекты, в том числе функцию, которую запускает на выполнение)
  • not — аналогично ok, но проверяет на ложность
  • group — позволяет группировать тесты в отдельные группы. Имеется возможность создавать любую иерархию групп.
  • groupClose — закрывает последнюю активную группу. (при вызове несколько раз подряд — будет последовательно закрывать активные группы)
  • comment — описывает комментарий к тесту
  • getHtmlResult — получает html результатов тестов
  • toConsole — выводит результаты тестов на консоль


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

Описание методов


comment({string}) — добавляет комментарий к следующему тесту.

getHtmlResult — предоставляет результаты тестов в виде html

toConsole — выводит результаты тестов в консоль

ok({object}) — принимает переменную\функцию\логическое выражение. В случае с функцией вызывает ее и обрабатывает результат выполнения).
Аналогично работает функция not
пример работы:

yats
    .comment('Это пример комментария').ok(1 === 1)
    .comment('Тестирование существования объекта').ok({someObjectField: 23})
    .comment('функция-аргумент метода ok будет вызвана').ok(function(){return true;})
    .comment('Это последний комментарий').not(function(){return false;})
    .comment('Это пример работы, когда тест не проходит').ok()
    .comment('Это пример работы, когда функция вызывает исключение').ok(function() {throw 'some exception';})
    .toConsole();




Успешно пройденные тесты помечаются синим цветом. Не прошедшие тесты или тесты, в которых был сгенерирован exception — оттенками красного.

group({string} название группы,{string} описание группы, {function} функция) — основной параметр — название группы. Помечает новую группу заданным названием. При необходимости можно задать дополнительное описание группы. (необязательно)
Также, функция, которая описывает группу тестов необязательна. Если такая группа определена, то все тесты внутри данной функции будут относится к выбранной группы (допустимы вложенности групп). По окончании функции, группа будет закрыта.
Если такую функцию не определять, то группу необходимо закрыть вручную с помощью функции groupClose().

Например, приведенные тесты идентичны:

yats
    // #1
    .group('ok/not')
        .comment('Пример теста внутри группы').ok(true)
    .groupClose()
    .toConsole()


    //Очищение стека тестов
    .clearStack()
    
    // #2
    .group('ok/not', function() {
            yats.comment('Пример теста внутри группы').ok(true)
        }
    )
    .toConsole();




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

Работа с группами:



Работа с группами выглядит следующим образом:
Разработчик получает возможность в файле описать работу каждого модуля, выделяя тесты для каждой функции в отдельную группу, например, у нас существует модуль cat:
var cat = {
    meow: function() {
        this.say = 'meow';
        this.action = 'say';
    },

    purr: function() {
        this.say = 'urrr';
        this.laught = true;
        this.action = 'say';
    },

    run: function() {
        this.action = 'run';
        this.say = '';
    }
};


Для тестирования данного модуля заведем группу тестов Cat:

yats.group('cat', function() {
// Код тестов
});


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

yats.group('cat', function() {

    cat.meow();
    yats
        .group('meow')
        .comment('что кот делает').ok(cat.action === 'say')
        .comment('что кот произносит').ok(cat.say === 'meow')
        .groupClose();

    cat.purr();
    yats
        .group('purr')
        .comment('что кот делает').ok(cat.action === 'say')
        .comment('что кот произносит').ok(cat.say === 'urrr')
        .comment('доволен ли кот').ok(cat.laught)
        .groupClose();

    cat.run();
    yats
        .group('run')
        .comment('что кот делает').ok(cat.action === 'run')
        .comment('что кот произносит').ok(cat.say === '')
        .groupClose();

});




Да, конечно, для данного примера необходимость создавать группу тестов для каждой функции притянута «за уши», но в случае, если для функции необхоидимо написать 3-5 и более тестов — перспектива обернуть эти тесты в группу кажется очень привлекательной. Мы получим более структурированный код и понятное отображение результатов теста.

Работа с DOM элементом.


YATS поддерживает «слежение» за DOM элементом. Для этого нам необходимо установить «рабочую ноду» и затем после каждой группы тестов данная нода будет очищаться.
Функции для работы с DOM элементом:

  • setWorkingNode({cssSelector}) — устанавливает рабочую ноду
  • resetWorkingNode() — сбрасывает рабочую ноду (более слежение за этим элементом не будет)
  • getWorkingNode() — получает рабочую ноды


Пример работы:

Предположим, что в html документе находится следующий код:
<div class="test-node">тест</div>


Код тестов:
yats
    .setWorkingNode('.test-node')
    .group('Пример работы с нодой', function() {

        yats
            .comment('Первая попытка получить innerHTML').ok(yats.getWorkingNode().innerHTML === 'тест')
            .comment('Внутри группы тестов рабочая нода не очищается').ok(yats.getWorkingNode().innerHTML === 'тест');

    })
    .toConsole()
    .group('Пример работы с нодой', function() {
        
        yats.comment('Получить не удается, так как данные в ноде уже удалены').not(yats.getWorkingNode().innerHTML);
        
    })
    .toConsole();




Работа с nodejs и браузерной консолью



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


(Подробное описание тестов хранится за фразой Tests)

Для node.js группы тестов и вложенности определены табуляцией, что позволяет упростить «навигацию» в просмотре результатов тестов:



Таким образом


В данной статье была описана необходимая информация для быстрого старта с помощью данной библиотеки.
Информацию о дополнительных методах (проверка переданных аргументов на эквивалентность (equal), работа с фиксированием переменной (test) и т.д.) можно найти в страничке-описании и в readme к проекту github.com/xnimorz/YATS
Tags:
Hubs:
+3
Comments27

Articles

Change theme settings