Тестирование с Ava.js
Сегодня мы все реже встречаем проекты, на которых нет требований к коду по части frontend. Несколько лет назад никто об этом даже не задумывался, а требования к кандидату были на уровне – «Формы верстать умеете? Вы приняты!». А сейчас любой уважающий себя и своих клиентов заказчик требует от разработчиков навык покрытия тестами своего кода. Казалось бы, зачем? Тестировать будут тестировщики, но не все так однозначно. По факту покрытие кода тестами решает множество проблем еще до этапа тестирования, что экономит не только время на разработку, но и на то самое тестирование. Продукт получается качественнее, разрабатывается быстрее и, как следствие, выходит дешевле для заказчика.
На данный момент существует великое множество библиотек и инструментов для тестирования. В большинстве случаев и проектов вы увидите такие инструменты, как Mocha, Jest и другие – все они популярны и хороши… Но неужели нет достойных альтернатив? Существует ли менее известная библиотека, которая может справиться со своими задачами лучше, чем вышеперечисленные флагманы? Зачастую для тестирования используют популярные инструменты, перечисленные выше.
Меня зовут Михаил, я JS-разработчик в SimbirSoft, и мне бы хотелось раскрыть такой малоизвестный, но очень полезный и понятный инструмент, как Ava. Это простая библиотека для запуска тестов на Node.js. В некоторых моментах она превосходит своих конкурентов, но почему-то пользуется гораздо меньшей популярностью. Возможно, для кого-то Ava может стать отличной альтернативой на старте нового проекта или решения вопроса с тестированием.
Статья будет полезна как новичкам, только погружающимся в тему написания тестов, так и опытным разработчикам для расширения кругозора. А если вы тимлид или техлид и стартуете новый проект, возможно, эта статья поможет вам закрыть вопрос «Что использовать в качестве альтернативы популярным инструментам для тестирования кода?».
Преимущества Ava.js
Начнем с того, зачем нам вообще писать тесты для своих приложений? Ведь ручное тестирование намного понятнее, удобнее и быстрее. Накидал в код кучу console.log
, кликнул туда, кликнул сюда, – вроде бы ничего не сломалось. Теперь кликнем сюда – запрос ушел, ответ пришел, ой… белый экран. Ошибка! Пошли разбираться.
А теперь представьте, что у вас несколько десятков, а то и сотня запросов или функций, которые необходимо протестировать. Как вы отнесетесь к тому, чтобы сделать это вручную? Наверное как-то так:
Теперь разберем, чем нам может помочь библиотека Ava, и какие у нее преимущества:
1) Элементарный синтаксис и простой API, который предоставляет только то, что вам нужно.
2) Удобный отчет о тестировании с указанием фрагмента кода, в котором произошла ошибка, и указаниями на конкретное различие между ожидаемым и фактическим результатами.
3) Скорость. Плюс данной библиотеки в том, что все тесты запускаются одновременно, что существенно сокращает время их выполнения.
4) Отсутствие неявных глобальных переменных.
Работа с библиотекой
Итак, создадим рабочую директорию для нашего тестового проекта, откроем терминал и введем следующие команды по очереди:
mkdir ava-test-app
cd ava-test-app
npm install -y
npm install --save-dev ava
Мы перешли в директорию, создали проект с настройками по умолчанию и установили Ava.
Далее открываем файл package.json
и видим примерно такую картину:
{
“version”: “1.0.0”,
“description”: “”,
“main”: “index.js”,
“scripts”: {
“test”: “echo \"Error: no test specified\" && exit 1”
},
“keywords”: [],
“author”: “”,
“license”: “ISC”,
“devDependencies”: {
“ava”: “^5.0.1”
}
}
В данном файле очищаем поле “test”
и записываем туда значение “ava”
:
. . .
“scripts”: {
“test”: “ava”
}
. . .
Таким образом, мы можем запускать тестирование с помощью библиотеки через команду npm test.
Наконец-то после установки библиотеки у нас есть возможность сделать свой первый тест. Создадим файл с расширением .js. и напишем в терминале:
echo > test.js
Переходим в этот файл и пишем наш тест:
import test from “ava”;
test(“Пройден первый тест!”, t => {
t.pass();
})
Далее в терминале прописываем:
npm test
Если вы все сделали правильно, то отчет о прошедшем тестировании должен быть таким:
> ava-test-app@1.0.0 test
> ava
√ Пройден первый тест!
1 test passed
Очевидно, что наш тест ничего не проверяет, а метод pass просто заставляет его быть пройденным. Существует также обратный метод – fail. Заменим в коде метод pass на fail.
import test from “ava”;
test(“Пройден первый тест!”, t => {
t.fail(); // Заменили метод на fail
})
Запускаем тест через npm test
и видим следующее:
> ava-test-app@1.0.0 test
> ava
✕ [fail]: Пройден первый тест! Test failed via ‘t.fail()’
Пройден первый тест!
test(“Пройден первый тест!”, t => {
t.fail();
})
Test failed via ‘t.fail()’
> file:///test.js
1 test failed
Тест завершился с ошибкой, и место, где она была совершена, подсвечивается
красным. Также мы видим описание причины, почему тест был провален: «Test failed via `t.fail()`». Думаю, что здесь даже перевода не нужно.
Попробуем что-нибудь посложнее. Создадим файл welcome.js
в этом же каталоге:
echo > welcome.js
C таким содержимым:
class Welcome {
}
export default Welcome;
Перепишем файл test.js
.
import test from “ava”;
import Welcome from “./welcome.js”;
test(“Пройден первый тест!”, t => {
t.is(“Hello, world!”, Welcome.hello());
})
Запускаем тест и видим:
>ava-test-app@1.0.0 test
>ava
✕ [fail]: Пройден первый тест! Error thrown in test
Пройден первый тест!
test(“Пройден первый тест!”, t => {
t.is(“Hello, world!”, Welcome.hello())
})
Error thrown in test:
TypeError {
message: ‘Welcome.hello is not a function’,
}
> file:///test.js
1 test failed
В консоли видим подсвеченную красным цветом строку, в которой произошла ошибка. Опускаемся чуть ниже, где содержится описание ошибки: “Welcome.hello is not a function”
. Проблема возникла из-за того, что у класса Welcome
отсутствует функция hello.
Так и быть, добавим:
class Welcome {
static hello() {
return “Hello, world!”
}
}
export default Welcome;
Запускаем тест:
> ava-test-app@1.0.0 test
> ava
√ Пройден первый тест!
1 test passed
Все работает! Мы также можем писать несколько независящих друг от друга тестов. Перепишем класс Welcome и добавим еще пару тестов в файл test.js:
class Welcome {
static hello(name = “world”) {
return `Hello, ${name}!`
}
}
export default Welcome;
import test from “ava”;
import Welcome from “./welcome.js”;
test(“Пройден первый тест!”, t => {
t.is(“Hello, world!”, Welcome.hello());
})
test(“Поприветствовали Ava.js”, t => {
t.is(“Hello, Ava!”, Welcome.hello(“Ava”));
})
test(“Поприветствовали JS”, t => {
t.is(“Hello, JavaScript!”, Welcome.hello(“Html”));
})
Получаем результат:
> ava-test-app@1.0.0 test
> ava
√ Пройден первый тест!
√ Поприветствовали Ava.js
✕ [fail]: Поприветствовали JS
Поприветствовали JS
test.js:13
test(“Поприветствовали JS”, t => {
t.is(“Hello, JavaScript!”, Welcome.hello(“Html”));
})
Difference (- actual, + expected):
- ‘Hello, Javascript!’
+ ‘Hello, Html!’
> file:///test.js:13:7
1 test failed
Библиотека предоставляет настолько простой синтаксис и очевиднейший отчет о тестировании, что разобраться во всем этом легко, даже если ты начинающий разработчик и на фотографиях получаешься именно таким:
Неочевидные преимущества Ava и сравнение библиотек
Рассмотрим еще несколько плюсов библиотеки Ava. Например, отсутствие неявных глобальных переменных. Сравним ее с другой библиотекой для тестирования – Mocha. Ниже один из возможных вариантов теста на Mocha.
var assert = require('assert');
describe('Array', function () {
describe('#indexOf()', function () {
it('should return -1 when the value is not present', function () {
assert.equal([1, 2, 3].indexOf(4), -1);
});
});
});
Мы видим, что тест явно определяет переменную assert. Но откуда взялись переменные describe
и it
? Это и есть те глобальные переменные, которые предоставляются нам библиотекой Mocha. Не будем углубляться в проблемы неявных глобальных переменных – это повод для отдельной статьи. Отмечу, что такие переменные сложно отслеживать, и это может затруднить отладку вашего кода.
У Ava таких проблем нет, потому что мы должны «требовать» все переменные.
Еще один несомненный плюс библиотеки – так называемые Magic assertions («Магические утверждения»). Когда вы запускали тесты выше, то наверняка заметили, что при провале теста Ava указывает нам на фактическое значение “actual”
и ожидаемое “expected”
. Это и есть «магические утверждения», которые помогают быстро понять, в чем именно заключается ошибка.
Поприветствовали JS:
test.js:13
test("Поприветствовали JS", t => {
t.is("Hello, Javascript!", Welcome.hello("Html"));
})
Difference (- actual, + expected):
- 'Hello, Javascript!'
+ 'Hello, Html!'
Библиотека прекрасно работает с промисами и ассинхронными функциями. Добавим еще пару тестов в наш файл test.js:
test("Асинхронные функции также работают:)", async t => {
const flag = await new Promise((resolve) => (
setTimeout(() => resolve(true), 1000)
))
t.true(flag);
})
test("И еще один!", async t => {
const batmanVoice = await new Promise((resolve) => (
setTimeout(() => resolve("I am Batman!"), 500)
))
t.is("I am Batman!", batmanVoice)
})
Все прекрасно работает:
✓ И еще один! (515ms)
✓ Асинхронные функции так же работают :) (1s)
Заключение
Можем сделать вывод, что у Ava есть очевидное преимущество перед многими другими библиотеками для тестирования (в частности, Mocha). В первую очередь, это элементарный синтаксис, который позволяет новичку в тестировании безболезненно начать покрывать свое приложение тестами, а в последствии – наработкой опыта перейти на более мощные инструменты.
Также стоит отметить еще один важный плюс Ava – отсутствие неявных глобальных переменных. Это очень помогает, особенно начинающим разработчикам, заниматься отладкой кода и не запутаться в обилии неявных переменных, появившихся неизвестно откуда.
Ava — это средство запуска тестов для Node.js, ориентированное на простое и легкое тестирование. Помимо понятного и лаконичного API с подробным выводом ошибок, Ava включает в себя новые языковые функции, которые позволяют создавать тесты более эффективным способом.
Полезные материалы для frontend-разработчиков мы также публикуем в наших соцсетях – ВК и Telegram.