- PVSM.RU - https://www.pvsm.ru -

AVA — Футуристическая JavaScript библиотека для тестирования

В этой статье я хочу представить вам новую библиотеку для тестирования AVA [1]. Относительно новую, ей уже больше 2-х лет, и она обзавелась солидным количеством плагинов и конечно же сообществом которое ее развивает. Мы посмотрим на функционал библиотеки. Настроим окружение и напишем пару тестов, чтобы посмотреть на библиотеку в действии.

Что же предлагает AVA?

В первую очередь библиотека предлагает скорость. Тесты запускаются параллельно, что дает ускорение выполнения тестов. В качестве примера приводится проект Pageres [2], в котором тестирование было перенесено на AVA [3], что дало увеличение почти в 3 раза(31 секунда было и 11 стало). Тесты не зависят от глобального состояния и от других тестов, что конечно же упрощает тестирование. Из коробки сразу идет использование es2015.

Что нужно сделать чтобы начать пользоваться AVA уже сейчас?

Установить соответствующий npm модуль. Установим как зависимость для работы в конкретной папке.

// package.json
  ...,
  "scripts": {
    "test": "ava"
  },
  ...

npm install -D ava
npm test

или глобально

npm i -g ava
ava

Запуск тестов

Настало время написать первый тест, возьмем пример из официального репозитория. И сохранить его как my-tests.js

import test from 'ava';

test('foo', t => {
    t.pass();
});

test('bar', async t => {
    const bar = Promise.resolve('bar');

    t.is(await bar, 'bar');
});

Сразу видим использование es2015 со стрелочными функциями, let и async. На мой взгляд, не обманули и действительно минималистичный синтаксис.

Запускаем тесты

npm test my-tests.js
// or
ava my-tests.js

И получаем результат

2 passed

Если мы хотим увидеть более подробную информацию о каждом тесте, мы можем использовать параметры для модуля

ava my-tests.js --verbose
// or
ava my-tests.js -v

   foo
   bar

  2 tests passed

Так же мы можем запустить watcher, чтобы разрабатывать в стиле TDD

ava my-tests.js --watch
// or
ava my-tests.js -w

Поспотреть полный список параметров можно

ava --help

API библиотеки

Простой тест:

test('description', t => {
});

Одна из самых распространенных ситуаций, когда нужно выполнить только один тест из всех:

test.only('test only', t => {
  t.pass();
});

Пропуск теста, может понадобиться при рефакторинге, поиске ошибки:

test.only('test only', t => {
  t.fail();
});

Заглушка для теста

Вынесено на уровень API, что очень интересно. Можно сделать напоминалку прямо в тестах.

test.todo('описание');

Если нам нужно протестировать асинхронную часть кода, мы можем воспользоваться "cb":

test.cb('callback', t => {
  setTimeout(function() {
    console.log('time');
    t.end();
  }, 3000);
});

Упорядоченное выполнение тестов

Параметр serial, позволит нам выполнять тесты в определенной последовательности. Например, мы хотим проверить существование конфигурационного файла. Если его нет, его нужно создать. Мы сделаем 2 теста, один будет создавать наш файл, а второй проверять.

И нам удобней будет, чтобы они запускались именно последовательно.

import test from 'ava';
import fs from 'fs';

const path = 'serial-test-one.txt';
test.cb('serial 1: create file', t => {
  fs.writeFile(path, 'test', function(err) {
    if (err) {
      t.fail();
    } else {
      t.pass();
    }
    t.end();
  });
});

test.cb('serial 2: is file exists', t => {
  fs.access(path, fs.F_OK, function(err) {
    if (err) {
      t.fail();
    } else {
      t.pass();
    }
    t.end();
  });
});

Написав такой код мы получаем

   serial-one › serial 2
   serial-one › serial 1

  2 tests passed

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

  - serial-one › serial 1
   serial-one › serial 2 Test failed via t.fail()

  1 test failed
  1 test skipped

  1. serial-one › serial 2
  AssertionError: Test failed via t.fail()
    serial-one.js:19:9
    FSReqWrap.oncomplete (fs.js:123:15)

Чтобы гарантировать последовательность мы можем использовать параметр --serial или -s

ava serial-one.js -s

   serial-one › serial 1
   serial-one › serial 2

  2 tests passed

Или использовать

test.cb.serial('serial 1', t => {
  ...
});

Тест падает и мы об этом знаем. Мы можем об этом явно указать.

test.failing('failing', t => {
  t.fail();
});

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

1 known failure

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

test.only.cb
test.cb.only

Before и After

Для настройки окружения тестов есть before и after. Они будет выполнены один раз на старте выполнения тестов и в конце соответственно.

test.before(t => {
});

test.after(t => {
});

Так же мы можем объявить несколько таких функций и они будет вызываться в порядке добавления

test.before(t => {
  console.log('before');
});

test.before(t => {
  console.log('before#2');
});

before
before#2

Работает и для after.

Если текст падает, after не вызываются. Чтобы исправить ситуацию нужно использовать модификатор always.

test.after.always(t => {
});

beforeEach и afterEach

Когда нам нужно настраивать окружение перед каждый тестом используем beforeEach и afterEach.

test.beforeEach(t => {
});
test.afterEach(t => {
});

Для них сохраняется поведение как и для before и after: порядок объявления и при ошибке в тесте after не вызываются(если нет always).

Assertions

test('test', t => {
  t.pass();
  t.skip.fail();
  t.truthy(true);
  t.truthy('unicorn');
  t.falsy(false);
  t.falsy(1 === 0);
  t.true(true);
  t.false(false);
  t.is(1, 1);
  t.not(1, 0);
  t.deepEqual([0, 1, 2], [0, 1, 2]);
  t.notDeepEqual([0, 2, 2], [0, 1, 2]);
});

Очень удобный deepEqual, и возможность пропустить проверку.
Отдельно рассмотрим вывод ошибок, он очень детальный.

test(t => {
    const a = /foo/;
    const b = 'bar';
    const c = 'baz';
    t.true(a.test(b) || b === c);
});

t.true(a.test(b) || b === c)
       |      |     |     |
       |      "bar" "bar" "baz"
       false

Что безусловно помогает отладке.

Плагины

До знакомства с AVA, я писал тесты на Jasmine [4]. Мне нравится Behavior-Driven style. Для этого в AVA есть плагин ava-spec [5].

npm i -D ava-spec

После чего мы можем писать тесты так

import {describe} from 'ava-spec';

describe('module#1', it => {
  it('can look almost like jasmine', t => {
    t.deepEqual([1, 2], [1, 2]);
  });

  it.todo('todo');

  it.skip('fail', t => {
    t.fail();
  });
});

TAP — Test Anything Protocol [6]

Мы можем кастомизировать информацию о наших тестах. Мне понравился tap-summary [7].

npm i -D tap-summary

Используем

ava ava-spec.js -t | tap-summary

Реальные модули для тестирования

Сделаем функция, положим ее в отдельный файл, подключим и протестируем.

// ./test/sum.spec.js
import { describe } from 'ava-spec';
import sum from '../src/sum';

describe('sum', it => {
  it('should return 10', t => {
    const expected = 10;
    const actual = sum(3, 7);

    t.is(actual, expected);
  });
});

// ./src/sum.js
function sum(x, y) {
  return x + y;
}

module.exports = sum;

ava test/sum.spec.js

Все работает, наш код из файла подключен и протестирован. Но есть проблема, наш код написал на ES5, а тесты ES6. Давайте исправим эту ситуацию. AVA из коробки использует Babeljs [8]. И для нашего кода мы будет тоже использовать его. Настраиваем конфиги.

// .babelrc
{
  "presets": [
    "es2015"
  ]
}

и для AVA. Конфиг AVA находится прямо в package.json.

// ./package.json
  ...,
  "ava": {
    "babel": "inherit",
    "require": [
      "babel-register"
    ]
  },
  ...

Запускаем без изменений.

ava test/sum.spec.js

Итог

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

Об авторе библиотеки

Хотелось бы чуть чуть рассказать про автора этой библиотеки. Это, наверное, один из самых известных в JS сообществе людей Синдре Сорхус [9]. На его странице на github вы можете посмотреть на его проекты и в клад в сообщество. И/или вы можете познакомиться с ним как с человеком, на сколько это возможно в интернете, или задать вопрос/ы через ama — Ask me anything! [10].

P.S.

  • В полезных ссылках последний репозиторий с примерами для статьи.
  • От себя: после знакомства с AVA, я перешел в своих новых домашних проектах на AVA. Следующий рабочий проект будет разрабатываться с этой библиотекой. Возможно некоторые старые свои проекты переведу на AVA заодно замерю скорость выполнения и сложность перехода.

Полезные ссылки:

Автор: alekseyleshko

Источник [14]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/javascript/202419

Ссылки в тексте:

[1] AVA: https://github.com/avajs/ava

[2] Pageres: https://github.com/sindresorhus/pageres

[3] тестирование было перенесено на AVA: https://github.com/sindresorhus/pageres/commit/663be15acb3dd2eb0f71b1956ef28c2cd3fdeed0

[4] Jasmine: http://jasmine.github.io/

[5] ava-spec: https://github.com/sheerun/ava-spec

[6] TAP — Test Anything Protocol: https://testanything.org/

[7] tap-summary: https://github.com/zoubin/tap-summary

[8] Babeljs: http://babeljs.io/

[9] Синдре Сорхус: https://github.com/sindresorhus

[10] ama — Ask me anything!: https://blog.sindresorhus.com/answering-anything-678ce5623798

[11] awesome-ava: https://github.com/avajs/awesome-ava

[12] awesome-tap: https://github.com/sindresorhus/awesome-tap

[13] Examples: https://github.com/AlekseyLeshko/ava-learn

[14] Источник: https://habrahabr.ru/post/313468/?utm_source=habrahabr&utm_medium=rss&utm_campaign=sandbox