Модульное тестирование с помощью Mocha | OTUS

Модульное тестирование с помощью Mocha

Для тестирования на JavaScript существует множество фреймворков. Если говорить о модульном тестировании, то одним из наиболее популярных является Mocha. Давайте посмотрим, как он работает.

Несколько слов о тестируемом приложении

Для примера возьмём React-приложение Calculator. Оно имеет два юнит-компонента: keypad и display. Это точно юниты, не зависящие от других юнитов. Кроме того, компоненты разделены на презентационные (уже упомянутые display и keypad) и компоненты-контейнеры — calculator-app — единственный компонент, имеющий state, и определяющий, что отображается на экране при нажатии на кнопку.

Но вышеописанный компонент отвечает только за логику отображения, а что насчёт вычислений? Ими занимается отдельный модуль calculator, не имеющий React-зависимостей. И такой модуль прекрасно подходит для юнит-тестирования, т. к. не содержит I/O- и UI-зависимостей.

Что касается отделения логики калькулятора от компонента React, то это было сделано путём выделения логики в модуль calculator. Это простейший модуль, принимающий состояние калькулятора (объект), а также символ (цифру либо оператор). И, разумеется, возвращающий новое состояние калькулятора. Но если каждое состояние зависит от предыдущего, как получить самое первое состояние? Довольно просто — модуль экспортирует initialState, которое вы применяете для инициализации. То есть состояние калькулятора не является неизвестным, а включает в себя поле display, которое и надо показать приложению калькулятора для данного состояния.

Вот, например, начало кода:

module.exports.initialState = { display: '0', initial: true }

module.exports.nextState = (calculatorState, character) => {
  if (isDigit(character)) {
    return addDigit(calculatorState, character)
  } else if (isOperator(character)) {
    return addOperator(calculatorState, character)
  } else if (isEqualSign(character)) {
    return compute(calculatorState)
  } else {
    return calculatorState
  }
}

//....

Здесь не так важна специфика алгоритма, как простота функции, которую экспортирует наш модуль, ведь, получив состояние, мы можем всегда проверить следующее.

Приступим к тестированию

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

Что касается Mocha, то здесь «Runner» представляет собой скрипт под названием mocha. Его можно легко увидеть, посмотрев на package.json в тестовом скрипте:

"scripts": {
...
    "test": "mocha 'test/**/test-*.js' && eslint test lib",
...
},

Это обеспечит запуск всех тестов в тестовой папке, названия которых начинаются с префикса test-. При запуске вы увидите приблизительно такой результат:

1_ZCXpZVRMgzZsCnaQMRZO4Q_1-20219-eeb47f.png

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

const {describe, it} = require('mocha')
const {expect} = require('chai')
const calculator = require('../../lib/calculator')

describe('calculator', function () {
  const stream = (characters, calculatorState = calculator.initialState) =>
    !characters
      ? calculatorState
      : stream(characters.slice(1),
               calculator.nextState(calculatorState, characters[0]))

  it('should show initial display correctly', () => {
    expect(calculator.initialState.display).to.equal('0')
  })
  it('should replace 0 in initialState', () => {
    expect(stream('4').display).to.equal('4')
  })
//...

Давайте первым делом импортируем mocha и библиотеку для проверок (assert’ов) expect. И выполним импорт нужных нам функций: describe, it, except. Далее выполним импорт тестируемого модуля — calculator.

Потом пойдут тесты, описанные с помощью функции it, допустим:

it('should show initial display correctly', () => {
    expect(calculator.initialState.display).to.equal('0')
})

Функция принимает строку, которая описывает тест, и функцию, которая и является непосредственно самим тестом. Однако it-тесты не могут быть «голыми», а должны быть в тестовых группах, определяемых посредством функции describe.

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

if (calculator.initialState.display !== '0')
  throw 'failed'

И такое решение бы подошло. Тест в Mocha не срабатывает, когда генерирует исключение, и это довольно просто. Однако expect делает тест намного приятнее, ведь в функции есть много фич, которые облегчают тестирование данных — к примеру, можно проверить, что массив либо объект равны конкретному значению.

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

Источник: "Testing Your Frontend Code: Part II (Unit Testing)".

Не пропустите новые полезные статьи!

Спасибо за подписку!

Мы отправили вам письмо для подтверждения вашего email.
С уважением, OTUS!

Автор
0 комментариев
Для комментирования необходимо авторизоваться