В Части 5 мы говорили о том, как мы можем использовать будущие функции JavaScript, компилируя наш код с помощью Babel, и чтобы увидеть это в действии, мы реорганизовали наш код, чтобы использовать классы ES6 . Теперь, когда мы возвращаемся к написанию кода, я думаю, что это идеальное время для введения тестов в наш проект, потому что тестирование делает всех счастливыми и заставляет нас лучше спать по ночам (не верьте мне на слово, вот некоторые Источники упоминание своих достоинств ). Итак, начнем!

Мы начнем с установки Mocha, тестовой среды, которую мы будем использовать в нашем проекте:

$ npm install mocha --save-dev

Хороший! Кроме того, поскольку мы пишем код на ES6, мы хотим писать и тесты таким же образом. Итак, как указано здесь, мы приступаем к установке babel-register:

$ npm install babel-register --save-dev

Затем мы настраиваем сценарий test в package.json (помните, поскольку мы не устанавливаем пакеты узлов глобально, мы должны указать относительный путь к двоичному файлу):

"test": "node_modules/.bin/mocha --compilers js:babel-register"

Теперь мы можем запускать тесты, выполнив:

$ npm test

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

Теперь, когда все настроено, мы можем написать несколько тестов, и мы будем тестировать наш класс Popup. Внутри каталога test мы создадим файл с именем popup.spec.js (вы можете называть файл как хотите, если он находится внутри каталога test, но таким образом помогает понять, что тестирует файл, просто взглянув на имя) со следующим содержанием:

describe('Popup', function() {
  describe('#alert', function() {
    describe('instance with default config', function() {
      describe('when invoked with "hello universe!"', function() {
        it('shows alert with "hello universe!"', function() {
          // assertion goes here
        });
      });
    });
  })
});

Так что это? Мы используем API Mocha для описания ожидаемого поведения в заданном контексте таким образом, который (надеюсь) понятен любому, кто читает тесты. В этом случае мы говорим, что хотим для класса Popup протестировать метод экземпляра alert, и когда этот метод вызывается со строкой « привет, вселенная! ”, в экземпляре, инициализированном конфигурацией по умолчанию, он должен отображать предупреждение с тем же текстом.

Теперь, когда мы выразили свои ожидания, мы хотим написать сам тест, но, к сожалению, наш метод alert сложно протестировать. Это связано с тем, что тесты, выполняемые с помощью интерфейса командной строки Mocha, выполняются в среде node, где глобальный объект window не существует (есть аналогичный объект в узле, который называется глобальным). Это означает, что нам нужно будет настроить объект window самостоятельно. Мы настроим его следующим образом:

describe('Popup', function() {
  if (typeof window === 'undefined') {
    before(function() {
      global.window = { alert() {} };
    });
    after(function() {
      delete global.window;
    });
  }
  // ...

Этим мы определяем, если window не определено, свойство с именем window в глобальном объекте node перед запуском тестов, а после запуска тестов мы удали это. Это достигается за счет использования хуков Mocha before и after, так что он запускается один раз при запуске этого тестового блока (это важно, чтобы не оставлять окружающую среду загрязненной для других тестов).

Как упомянул Мэтт Джонс (еще раз спасибо), вместо того, чтобы вручную настраивать среду браузера внутри узла, мы можем использовать для этого библиотеку под названием jsdom, и есть даже библиотека, чтобы легко интегрировать ее в Мокко, называется mocha-jsdom, так что зацените.

Хорошо, но у нас есть еще одна большая проблема, поскольку alert не является методом, который возвращает что-либо, против чего мы можем утверждать. Все, что мы знаем, это то, что он использует window.alert для отображения окна предупреждения ... Итак, что мы можем сделать, так это подтвердить, что вызывается window.alert, и мы это сделаем шпионя за ним с помощью Sinon. Установите его с помощью:

$ npm install [email protected] --save-dev

Мы указываем версию Sinon, которую хотим установить, потому что на момент написания npm устанавливает версию 1.17.3, с которой не работает. webpack (и это то, что нам понадобится). После установки настройте его на слежку за window.alert, например:

import sinon from 'sinon';
// ...
  describe('instance with default config', function() {
    let alertSpy;
    beforeEach(function() {
      alertSpy = sinon.spy(window, 'alert');
    });
    afterEach(function() {
      alertSpy.restore();
    });

Этим мы говорим Mocha перед запуском каждого теста внутри блока настроить шпион на метод window's alert (ловушка beforeEach) через Sinon, и после запуска каждого теста мы хотим восстановить исходный метод alert (ловушка afterEach). Теперь, когда нам удалось решить эти две большие проблемы, мы почти приехали! Нам просто нужно утверждать, что при вызове Popup # alert вызывается window.alert с правильной строкой. Отредактируйте файл следующим образом:

import sinon from 'sinon';
import Popup from './../src/popup.js';
import assert from 'assert';
    // ...
      describe('when invoked with "hello universe!"', function() {
        it('shows alert with text "hello universe!"', function() {
          const popup = new Popup();
          popup.alert('hello universe!');
 
          assert(alertSpy.calledWith('hello universe!'));
        });
      });
    // ...

И, наконец, мы добавили наш тест, который проверяет, был ли вызван window.alert с правильной строкой. Если мы запустим этот тест, он не сработает, потому что у нас нет файла с именем src / popup.js, поэтому мы воспользуемся этой возможностью, чтобы извлечь класс Popup в его собственный модуль. Создайте файл src / popup.js со следующим кодом:

import _ from 'lodash';
export default class Popup {
  constructor(config = {}) {
    this.capitalize = !!config.capitalize;
  }
  alert(text) {
    const output = this.capitalize ? _.capitalize(text) : text;
    window.alert(output); // eslint-disable-line no-alert
  }
}

Обратите внимание: поскольку мы используем Babel, теперь мы будем экспортировать, используя export default вместо module.exports, поэтому мы с использованием функций ES6 вместо узлов. Осталось отредактировать src / index.js:

import Popup from './popup.js';
const popup = new Popup({ capitalize: true });
popup.alert('hello universe!');

Осталось запустить тесты:

Отличная работа! Это было долго, и даже несмотря на то, что есть о чем поговорить, мы оставим это для другого поста. Как всегда, Mocha и Sinon больше, чем было описано здесь, так что зайдите на их домашние страницы, чтобы узнать больше. Также стиль, в котором мы пишем тесты, как и любой код, который мы пишем, варьируется от человека к человеку, поэтому, если вы напишете его по-другому, не стесняйтесь оставлять комментарий.

Сегодняшний код доступен здесь (я опустил некоторые части в этом сообщении для краткости, поэтому код в репозитории немного отличается).

Спасибо за чтение!

PS: Часть 7 окончена.