сборка

Чем запускать тесты

Test runner — это программа, которая запускает тесты.

Есть много популярных программ запуска тестов для JavaScript, и vue-test-utils работает с любой из них. Нет зависимости от используемого test runner.

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

  • Jest наиболее функциональный способ запуска тестов. Он меньше всех требует конфигурирования, устанавливает JSDOM по умолчанию, предоставляет встроенные проверки (assertions) и имеет отличный интерфейс для работы через командную строку. Тем не менее, вам понадобится пре-процессор, чтобы иметь возможность импортировать однофайловые компоненты в свои тесты. Мы создали пре-процессор vue-jest, который может обрабатывать наиболее распространённые функции однофайловых компонентов, но в настоящее время он не имеет 100% паритетности функций с vue-loader.

  • mocha-webpack — это обёртка вокруг webpack + Mocha, но с более оптимизированным интерфейсом и режимом отслеживания. Преимущества этой конфигурации в том, что мы можем получить полную поддержку однофайловых компонентов с помощью webpack + vue-loader, но для этого требуется больше настройки.

Браузерное окружение

vue-test-utils полагается на браузерное окружение. Технически вы можете запустить его в реальном браузере, но это не рекомендуется из-за сложности запуска реальных браузеров на разных платформах. Вместо этого мы рекомендуем запускать тесты в Node с виртуальным браузерным окружением, реализуемым с помощью JSDOM.

Jest настраивает JSDOM автоматически. Для других программ запуска тестов вы можете вручную настроить JSDOM для тестов с помощью jsdom-global в записи для ваших тестов:

npm install --save-dev jsdom jsdom-global

// в настройке теста / entry
require('jsdom-global')()

Тестирование однофайловых компонентов

Однофайловые компоненты Vue требуют предварительной компиляции, прежде чем могут быть запущены в Node или в браузере. Существует два рекомендуемых способа выполнения компиляции: с пре-процессором Jest, или непосредственно с помощью webpack.

Пре-процессор vue-jest поддерживает базовую функциональность однофайловых компонентов, но в настоящее время не обрабатывает блоки стилей или пользовательские блоки, которые поддерживаются только в vue-loader. Если вы полагаетесь на эти функции или другие конфигурации, специфичные для webpack, вам нужно будет использовать связку webpack + vue-loader.

Изучите следующие руководства по вариантам настройки:

Дополнительные ресурсы

Тестирование однофайловых компонентов с Jest

Пример проекта для этой конфигурации доступен на GitHub.

Jest — это программа для запуска тестов, разработанная Facebook, направленная на предоставление функционального решения для модульного тестирования. Вы можете узнать больше о Jest в официальной документации.

Установка Jest

Предположим, что вы начинаете с конфигурации, где правильно настроены webpack, vue-loader и Babel — например, развёрнутый шаблон webpack-simple с помощью vue-cli.

Первым делом нам необходимо установить Jest и vue-test-utils:

$ npm install --save-dev jest @vue/test-utils

Затем, необходимо указать псевдоним для запуска тестов в нашем package.json.

// package.json
{
  "scripts": {
    "test": "jest"
  }
}

Обработка однофайловых компонентов с Jest

Чтобы научить Jest как обрабатывать *.vue файлы, нам необходимо установить и настроить пре-процессор vue-jest:

npm install --save-dev vue-jest

Теперь, создадим секцию jest в файле package.json:

{
  // ...
  "jest": {
    "moduleFileExtensions": [
      "js",
      "json",
      // сообщаем Jest что необходимо обрабатывать `*.vue` файлы
      "vue"
    ],
    "transform": {
      // обрабатываем `*.vue` файлы с помощью `vue-jest`
      ".*\\.(vue)$": "<rootDir>/node_modules/vue-jest"
    }
  }
}

Примечание: vue-jest в настоящее время не поддерживает все возможности vue-loader, например пользовательские блоки и загрузку стилей. Кроме того, некоторые функции, специфичные для webpack, такие как code-splitting, также не поддерживаются. Чтобы использовать их прочитайте руководство по тестированию однофайловых компонентов с Mocha + webpack.

Обработка псевдонимов webpack

Если вы используете псевдонимы в конфигурации webpack, например когда @ ссылается на путь /src, вам также нужно добавить соответствующую конфигурацию для Jest, используя опцию moduleNameMapper:

{
  // ...
  "jest": {
    // ...
    // добавление поддержки псевдонима @ -> src в исходном коде
    "moduleNameMapper": {
      "^@/(.*)$": "<rootDir>/src/$1"
    }
  }
}

Конфигурация Babel для Jest

Хотя последние версии Node уже поддерживают большинство функций ES2015, вы всё равно можете использовать синтаксис ES-модулей и stage-x функции в ваших тестах. Для этого нужно установить babel-jest:

npm install --save-dev babel-jest

Затем мы должны сообщить Jest обрабатывать файлы тестов с JavaScript с помощью babel-jest, добавив запись jest.transform в package.json:

{
  // ...
  "jest": {
    // ...
    "transform": {
      // ...
      // обрабатывать js с помощью `babel-jest`
      "^.+\\.js$": "<rootDir>/node_modules/babel-jest"
    }
    // ...
  }
}

По умолчанию babel-jest автоматически настраивается по установке. Однако, поскольку мы явно добавили преобразование файлов *.vue, нам теперь нужно также настроить babel-jest.

Предполагая использование babel-preset-env с webpack, конфигурация Babel по умолчанию отключает транспиляцию ES-модулей, потому что webpack уже знает как обрабатывать ES-модули. Однако нам нужно включить его для наших тестов, потому что тесты Jest запускаются непосредственно в Node.

Кроме того, мы можем указать babel-preset-env в качестве цели используемую нами версию Node. Это пропустит транспиляцию ненужных функций и ускорит загрузку тестов.

Чтобы применить эти параметры только для тестов, поместите их в отдельную конфигурацию в env.test (это будет автоматически обработано babel-jest).

Пример .babelrc:

{
  "presets": [["env", { "modules": false }]],
  "env": {
    "test": {
      "presets": [["env", { "targets": { "node": "current" } }]]
    }
  }
}

Расположение файлов тестов

По умолчанию Jest будет рекурсивно выбирать все файлы с расширением .spec.js или .test.js во всём проекте. Если это поведение не соответствует вашим потребностям, то возможно изменить testRegex в секции конфигурации в файле package.json.

Jest рекомендует создать каталог __tests__ рядом с тестируемым кодом, но не стесняйтесь структурировать ваши тесты по своему усмотрению. Просто остерегайтесь того, что Jest создаст каталог __snapshots__ рядом с тестовыми файлами, который необходим для тестирования с помощью моментальных снимков.

Покрытие кода (Coverage)

Jest может быть использован для генерации отчётов о покрытии кода в нескольких форматах. Ниже приведён простой пример для начала:

Расширьте вашу конфигурацию jest (обычно расположенную в package.json или jest.config.js) с помощью опции collectCoverage, и затем добавьте массив collectCoverageFrom для определения файлов, для которых требуется собирать информацию о покрытии.

{
  "jest": {
    // ...
    "collectCoverage": true,
    "collectCoverageFrom": ["**/*.{js,vue}", "!**/node_modules/**"]
  }
}

Это включит отчёты о покрытии с использованием стандартных отчётов о покрытии. Вы можете настроить их с помощью опции coverageReporters:

{
  "jest": {
    // ...
    "coverageReporters": ["html", "text-summary"]
  }
}

Дополнительную информацию можно найти в документации по конфигурации Jest, где вы можете найти параметры для пороговых значений покрытия, каталоги вывода данных и т.д.

Пример спецификации

Если вы знакомы с Jasmine, то вы должны чувствовать себя как дома с проверочным API Jest:

import { mount } from '@vue/test-utils'
import Component from './component'

describe('Component', () => {
  test('является экземпляром Vue', () => {
    const wrapper = mount(Component)
    expect(wrapper.isVueInstance()).toBeTruthy()
  })
})

Тестирование моментальными снимками

Когда вы монтируете компонент с помощью Vue Test Utils, вы получаете доступ к корневому элементу HTML. Это можно сохранить в виде моментального снимка для тестирования моментальными снимками в Jest:

test('renders correctly', () => {
  const wrapper = mount(Component)
  expect(wrapper.element).toMatchSnapshot()
})

Мы можем улучшить сохраненный снимок с помощью пользовательского сериализатора:

npm install --save-dev jest-serializer-vue

Затем настройте jest-serializer-vue в package.json:

{
  // ...
  "jest": {
    // ...
    // сериализатор для снимков
    "snapshotSerializers": ["jest-serializer-vue"]
  }
}

Ресурсы

Тестирование однофайловых компонентов с Mocha и webpack

Пример проекта для этой конфигурации доступен на GitHub.

Другая стратегия тестирования однофайловых компонентов заключается в компиляции всех наших тестов с помощью webpack, а затем программой для запуска тестов. Преимущество такого подхода заключается в том, что он даёт нам полную поддержку всех функций webpack и vue-loader, поэтому нам не нужно идти на компромиссы в нашем исходном коде.

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

Настройка mocha-webpack

Мы предположим, что вы начинаете с настройки, когда уже есть правильно настроенные webpack, vue-loader и Babel — например используя шаблон webpack-simple, развёрнутый с помощью vue-cli.

Первое, что нужно сделать, это установить тестовые зависимости:

npm install --save-dev @vue/test-utils mocha mochapack

Затем мы должны указать скрипт test в нашем package.json.

// package.json
{
  "scripts": {
    "test": "mochapack --webpack-config webpack.config.js --require test/setup.js test/**/*.spec.js"
  }
}

Несколько вещей, о том что мы сделали:

  • Флаг --webpack-config указывает конфигурационный файл webpack для использования в тестах. В большинстве случаев это будут идентичная конфигурация, используемой в проекте, с одной небольшой доработкой. Мы поговорим об этом позднее.

  • Флаг --require гарантирует, что файл test/setup.js будет запущен перед любыми тестами, в котором мы можем настроить для наших тестов глобальное окружение, в котором они будут запускаться.

  • Последний аргумент — это шаблон для тестовых файлов, которые будут включены в тестовую сборку.

Дополнительная конфигурация webpack

Вынесение внешних NPM-зависимостей

В наших тестах мы, скорее всего, импортируем ряд NPM-зависимостей — некоторые из этих модулей могут быть написаны не для использования в браузере и просто не смогут быть корректно добавлены в сборку webpack. Другой плюс в том, что извлечение внешних зависимостей значительно улучшит скорость загрузки тестов. Мы можем вынести все NPM-зависимости с помощью webpack-node-externals:

// webpack.config.js
const nodeExternals = require('webpack-node-externals')

module.exports = {
  // ...
  externals: [nodeExternals()]
}

Source Maps

Source maps должны быть встроены для использования в mochapack. Рекомендуемая конфигурация:

module.exports = {
  // ...
  devtool: 'inline-cheap-module-source-map'
}

При отладке через IDE рекомендуется также добавить следующее:

module.exports = {
  // ...
  output: {
    // ...
    // использовать абсолютные пути в sourcemaps (важно для отладки через IDE)
    devtoolModuleFilenameTemplate: '[absolute-resource-path]',
    devtoolFallbackModuleFilenameTemplate: '[absolute-resource-path]?[hash]'
  }
}

Настройка браузерного окружения

vue-test-utils требует браузерного окружения для запуска. Мы можем симулировать его в Node используя jsdom-global:

npm install --save-dev jsdom jsdom-global

Затем в test/setup.js:

require('jsdom-global')()

Это добавит браузерное окружение в Node, таким образом vue-test-utils сможет корректно запуститься.

Выбор библиотеки утверждений

Chai — популярная библиотека утверждений, которая обычно используется вместе с Mocha. Вы также можете воспользоваться Sinon для создания шпионов и заглушек.

В качестве альтернативы вы можете использовать expect, который является частью Jest и реализует точно такой же API в документации Jest.

Мы будем использовать expect здесь и сделаем его глобально доступным, чтобы нам не приходилось импортировать его в каждом тесте:

npm install --save-dev expect

Затем в test/setup.js:

require('jsdom-global')()

global.expect = require('expect')

Оптимизация Babel для тестов

Обратите внимание, что мы используем babel-loader для обработки JavaScript. У вас уже должен быть настроен Babel, если вы используете его в своём приложении, через файл .babelrc. Здесь babel-loader будет автоматически использовать тот же файл конфигурации.

Следует отметить, что если вы используете Node 6+, которая уже поддерживает большинство функций ES2015, вы можете настроить отдельную опцию env Babel, которая будет транспилировать только те функции, которые ещё не поддерживаются в используемой версии Node (например, stage-2 или поддержку синтаксиса flow, и т.п.).

Добавление теста

Создайте файл в src названный Counter.vue:

<template>
  <div>
    {{ count }}
    <button @click="increment">Увеличить</button>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        count: 0
      }
    },

    methods: {
      increment() {
        this.count++
      }
    }
  }
</script>

И создайте файл теста, названный test/Counter.spec.js со следующим кодом:

import { shallowMount } from '@vue/test-utils'
import Counter from '../src/Counter.vue'

describe('Counter.vue', () => {
  it('увеличивает счётчик по нажатию кнопки', () => {
    const wrapper = shallowMount(Counter)
    wrapper.find('button').trigger('click')
    expect(wrapper.find('div').text()).toMatch('1')
  })
})

И теперь мы можем запустить тест:

npm run test

Ура, мы запустили наши тесты!

Покрытие кода (Coverage)

Для настройки покрытия кода в mochapack, следуйте инструкции по настройке покрытия кода mochapack.

Ресурсы

Тестирование однофайловых компонентов с Karma

Пример проекта для этой конфигурации доступен на GitHub.

Karma — это программа для запуска тестов, которая открывает браузеры, выполняет тесты и сообщает нам об их результатах. Мы собираемся использовать фреймворк Mocha для написания тестов. Мы будем использовать библиотеку chai для тестовых утверждений.

Установка Mocha

Предположим, что вы начинаете с конфигурации, где правильно настроены webpack, vue-loader и Babel — например, развёрнутый шаблон webpack-simple с помощью vue-cli.

Первым делом нам необходимо установить тестовые зависимости:

npm install --save-dev @vue/test-utils karma karma-chrome-launcher karma-mocha karma-sourcemap-loader karma-spec-reporter karma-webpack mocha

Далее нам нужно определить скрипт для запуска тестов в package.json.

// package.json
{
  "scripts": {
    "test": "karma start --single-run"
  }
}
  • Флаг --single-run указывает Karma запускать набор тестов только один раз.

Karma Configuration

Создайте файл karma.conf.js в корне вашего проекта:

// karma.conf.js

var webpackConfig = require('./webpack.config.js')

module.exports = function(config) {
  config.set({
    frameworks: ['mocha'],

    files: ['test/**/*.spec.js'],

    preprocessors: {
      '**/*.spec.js': ['webpack', 'sourcemap']
    },

    webpack: webpackConfig,

    reporters: ['spec'],

    browsers: ['Chrome']
  })
}

Этот файл используется для настройки Karma.

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

В нашей конфигурации мы запускаем тесты в Chrome. Для добавления дополнительных браузеров смотрите раздел Браузеры в документации Karma.

Выбор библиотеки утверждений

Chai - популярная библиотека утверждений, которая обычно используется вместе с Mocha. Вы посмотреть на Sinon для создания шпионов и заглушек.

Мы можем установить плагин karma-chai для использования chai в наших тестах.

npm install --save-dev karma-chai

Добавление теста

Создайте в каталоге src файл с именем Counter.vue:

<template>
  <div>
    {{ count }}
    <button @click="increment">Increment</button>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        count: 0
      }
    },

    methods: {
      increment() {
        this.count++
      }
    }
  }
</script>

И создайте файл test/Counter.spec.js со следующим кодом:

import { expect } from 'chai'
import { shallowMount } from '@vue/test-utils'
import Counter from '../src/Counter.vue'

describe('Counter.vue', () => {
  it('increments count when button is clicked', () => {
    const wrapper = shallowMount(Counter)
    wrapper.find('button').trigger('click')
    expect(wrapper.find('div').text()).contains('1')
  })
})

И теперь мы можем запустить тесты:

npm run test

Ура, наши тесты работают!

Покрытие кода

Для настройки покрытия кода Karma, мы можем использовать плагин karma-coverage.

По умолчанию karma-coverage не будет использовать исходные карты для отображения отчётов о покрытии. Поэтому нам нужно использовать babel-plugin-istanbul, чтобы убедиться, что покрытие правильно работает.

Установите karma-coverage, babel-plugin-istanbul и cross-env:

npm install --save-dev karma-coverage cross-env

Мы собираемся использовать cross-env для установки переменной окружения BABEL_ENV. Таким образом, мы можем использовать babel-plugin-istanbul при компиляции наших тестов — мы не хотим включать babel-plugin-istnabul при компиляции нашего кода в production:

npm install --save-dev babel-plugin-istanbul

Обновите файл .babelrc для использования babel-plugin-istanbul, когда BABEL_ENV равняется test:

{
  "presets": [["env", { "modules": false }], "stage-3"],
  "env": {
    "test": {
      "plugins": ["istanbul"]
    }
  }
}

Теперь обновите файл karma.conf.js для использования покрытия кода. Добавьте coverage в массив reporters и добавьте поле coverageReporter:

// karma.conf.js

module.exports = function(config) {
  config.set({
    // ...

    reporters: ['spec', 'coverage'],

    coverageReporter: {
      dir: './coverage',
      reporters: [{ type: 'lcov', subdir: '.' }, { type: 'text-summary' }]
    }
  })
}

И обновите тестовый скрипт test для установки BABEL_ENV:

// package.json
{
  "scripts": {
    "test": "cross-env BABEL_ENV=test karma start --single-run"
  }
}

Ресурсы