Автор этой статьи:речная вода
Эта статья в основном знакомит с интерфейсоммодульный тестнекоторые технические решения.
Многие модульные тестирующие технические решения, существуют синергизм между различными инструментами друг друга, есть также функции перекрываются, чтобы не дать нам небольшие трудности с программой тестирования, а также сES6
, TypeScript
Появление юнит-тестов добавляет массу других шагов, полная настройка которых зачастую требует больших временных затрат. Я надеюсь, что благодаря освоению соответствующих ролей этих инструментов для полного внешнего тестирования технических решений. Переднего конца полевого модульного тестирования много, здесь в основном речь идет о том, как внешние компоненты для модульного тестирования, и, наконец, в основном будут представлены дляReact
Краткое изложение некоторых методов тестирования компонентов.
Универсальный тест
Основная часть модульного тестирования состоит в том, чтобы делать утверждения, например, в традиционных языках.assert
функция, если определенное состояние текущей программы соответствуетassert
Ожидается, что эта программа может выполняться нормально, в противном случае приложение будет закрыто напрямую. Таким образом, мы можем напрямую использоватьNode
включен вassert
Модули делают утверждения.
Проверить на простейшем примере
function multiple(a, b) {
let result = 0;
for (let i = 0; i < b; ++i)
result += a;
return result;
}
const assert = require('assert');
assert.equal(multiple(1, 2), 3));
Этот пример подходит для использования в базовых сценариях, а также может использоваться в качестве метода модульного тестирования.
nodejs
автономныйassert
Модуль предоставляет следующие методы утверждения, которые могут удовлетворить потребности только некоторых простых сценариев.
assert.deepEqual(actual, expected[, message])
assert.deepStrictEqual(actual, expected[, message])
assert.doesNotMatch(string, regexp[, message])
assert.doesNotReject(asyncFn[, error][, message])
assert.doesNotThrow(fn[, error][, message])
assert.equal(actual, expected[, message])
assert.fail([message])
assert.ifError(value)
assert.match(string, regexp[, message])
assert.notDeepEqual(actual, expected[, message])
assert.notDeepStrictEqual(actual, expected[, message])
assert.notEqual(actual, expected[, message])
assert.notStrictEqual(actual, expected[, message])
assert.ok(value[, message])
assert.rejects(asyncFn[, error][, message])
assert.strictEqual(actual, expected[, message])
assert.throws(fn[, error][, message])
автономныйassert
Он не используется специально для модульного тестирования, а предоставляемая информация об ошибках плохо документирована.demo
Окончательное выполнение выдаст следующий отчет:
$ node index.js
assert.js:84
throw new AssertionError(obj);
^
AssertionError [ERR_ASSERTION]: 2 == 3
at Object.<anonymous> (/home/quanwei/git/index.js:4:8)
at Module._compile (internal/modules/cjs/loader.js:778:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
at startup (internal/bootstrap/node.js:283:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:623:3)
Из-за встроенных зависимостей модуляNode
Его собственная версия не может быть обновлена свободно, поэтому гибкости использования встроенного пакета иногда недостаточно.Кроме того, многие наши функции утверждений также необходимо выполнять на стороне браузера, поэтому нам необходимо поддерживать как браузер, вNode
библиотека побочных утверждений. Глядя одновременно на приведенный выше вывод, вы можете видеть, что этот отчет больше похож на отчет об ошибке программы, чем на отчет модульного тестирования. Когда мы проводим модульное тестирование, нам часто нужна библиотека утверждений для предоставления хороших отчетов о тестировании, чтобы мы могли сразу увидеть, какие утверждения проходят, а какие нет, поэтому по-прежнему необходимо использовать профессиональную библиотеку утверждений для модульных тестов.
chai
chai
Это очень популярная библиотека утверждений, которая более заметна, чем аналогичные продукты.chai
при условииTDD(Разработка через тестирование) иBDD(Разработка, основанная на поведении) Существует два стиля функций утверждений. Мы не будем вводить здесь преимущества и недостатки этих двух стилей. Эта статья в основном посвященаBDD
стиль презентации.
Чай в стиле TDD
var assert = require('chai').assert
, foo = 'bar'
, beverages = { tea: [ 'chai', 'matcha', 'oolong' ] };
assert.typeOf(foo, 'string'); // without optional message
assert.typeOf(foo, 'number', 'foo is a number'); // with optional message
assert.equal(foo, 'bar', 'foo equal `bar`');
assert.lengthOf(foo, 3, 'foo`s value has a length of 3');
assert.lengthOf(beverages.tea, 3, 'beverages has 3 types of tea');
chai
СравниватьNode
автономныйassert
Добавлен параметр описания утверждения, который может улучшить читаемость отчета о тестировании.
$ node chai-assert.js
/home/quanwei/git/learn-tdd-bdd/node_modules/chai/lib/chai/assertion.js:141
throw new AssertionError(msg, {
^
AssertionError: foo is a number: expected 'bar' to be a number
at Object.<anonymous> (/home/quanwei/git/learn-tdd-bdd/chai-assert.js:6:8)
at Module._compile (internal/modules/cjs/loader.js:778:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
at startup (internal/bootstrap/node.js:283:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:623:3)
Чай в стиле BDD
chai
изBDD
использование стиляexpect
Как начало семантики функции также почти всеBDD
Все библиотеки инструментов следуют этому стилю.
chai
изexpect
Стиль утверждения следующий
expect(foo).to.be.a('string');
expect(foo).to.equal('bar');
expect(foo).to.have.lengthOf(3);
BDD
Идея состоит в том, что написание модульных тестов похоже на написание требований к продукту, не заботясь о внутренней логике, и каждый вариант использования читается как документ. Например, следующий вариант использования:
- foo это строка ->
expect(foo).to.be.a('string')
- строка foo содержит 'bar' ->
expect(foo).to.include('bar')
- строка foo не содержит 'biz' ->
expect(foo).to.not.include('biz')
Вы можете видеть, что этот стиль тестовых примеров более удобочитаем.
Другие библиотеки утвержденийexpect.js
should.js
better-assert , unexpected.jsЭти библиотеки утверждений предоставляют только чистые функции утверждений, и вы можете выбирать различные библиотеки для использования в соответствии со своими предпочтениями.
После того, как у нас есть библиотека утверждений, нам также нужно использовать среду тестирования, чтобы лучше организовать наши утверждения.
мокко и жасмин
mocha
Это классический тестовый фреймворк (Test Framework).Тестовый фреймворк предоставляет основу для модульного тестирования, которая может разделить различные подфункции на несколько файлов или выполнять различные функциональные тесты для разных подфункций подмодуля для генерации A структурированный отчет об испытаниях. Напримерmocha
при условииdescribe
а такжеit
Опишите структуру прецедентов, предоставивbefore
, after
, beforeEach
, afterEach
Функции жизненного цикла, которые обеспечиваютdescribe.only
,describe.skip
, it.only
, it.skip
Используется для выполнения указанной части набора тестов.
const { expect } = require('chai');
const { multiple } = require('./index');
describe('Multiple', () => {
it ('should be a function', () => {
expect(multiple).to.be.a('function');
})
it ('expect 2 * 3 = 6', () => {
expect(multiple(2, 3)).to.be.equal(6);
})
})
Платформа тестирования не зависит от базовой библиотеки утверждений, даже если используется собственныйassert
Также доступны модули. Вручную импортировать каждый файлchai
Это более хлопотно, в это время вы можете датьmocha
Настройте глобальные сценарии в корневом каталоге проекта..mocharc.js
Загрузите библиотеку утверждений в файл, чтобы каждый файл можно было использовать напрямую.expect
функция.
// .mocharc.js
global.expect = require('chai').expect;
Использование mocha может вывести наши модульные тесты в хороший отчет о тестировании.mocha *.test.js
Когда есть ошибка, вывод выглядит следующим образом
Поскольку форматы пакетов, необходимые для запуска в разных средах, различаются, нам нужно выполнять разные преобразования формата пакетов для разных сред.Чтобы понять, что нужно сделать для запуска модульных тестов на разных концах, вы должны сначала понять общие форматы пакетов. .
В настоящее время у нас есть три основных формата модулей:AMD
, CommonJS
, ES Module
.
AMD
AMDдаRequireJS
Более старая спецификация, популярная в процессе продвижения. В настоящее время, будь то браузеры илиNode
Ни один из них не поддерживается по умолчанию.AMD
Стандарт определяетdefine
а такжеrequire
функция,define
Используется для определения модулей и их зависимостей,require
Используется для загрузки модулей. Например
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Document</title>
+ <script
+ src="https://requirejs.org/docs/release/2.3.6/minified/require.js"></script>
+ <script src="./index.js" />
</head>
<body></body>
</html>
// index.js
define('moduleA', ['https://some/of/cdn/path'], function() {
return { name: 'moduleA' };
});
define(function(require) {
const fs = require('fs');
return fs;
})
define('moduleB', function() {
return { name: 'module B' }
});
require(['moduleA', 'moduleB'], function(moduleA, moduleB) {
console.log(module);
});
используется здесьRequireJS
так какAMD
двигатель видноdefine
Эта функция определяет, от каких модулей в данный момент зависят, и асинхронно обращается к текущему модулю после его загрузки.Эта функция делает AMD особенно подходящей для асинхронной загрузки на стороне браузера.
мы можем использоватьwebpack
упаковать одинamd
Посмотрите на реальный код модуля
// entry.js
export default function sayHello() {
return 'hello amd';
}
// webpack.config.js
module.exports = {
mode: 'development',
devtool: false,
entry: './entry.js',
output: {
libraryTarget: 'amd'
}
}
Окончательный сгенерированный код (упрощает ненужную логику)
// dist/main.js
define(() => ({
default: function sayHello() {
return 'hello amd';
}
}));
в браузере/Node
хочу использоватьAMD
нужно импортировать глобальноRequireJS
, типичная проблема модульного тестирования заключается в инициализацииkarma
Когда вас спросят, использовать лиRequireJS
, но вообще мало кто сейчас им пользуется.
CommonJS
можно сократить какCJS
, ЭтоТехнические характеристикиглавным образом для определенияNode
формат упаковки,CJS
Определены три ключевых слова, а именноrequire
,exports
, module
, почти всеNode
пакеты и внешний интерфейсNPM
пакет будет конвертирован в этот формат,CJS
Необходимо использовать на стороне браузераwebpack
илиbrowserify
Подождите, пока инструмент будет упакован.
ES Module
ES Module
даES 2015
Спецификация модуля, определенная в , которая определяет представление какimport
а такжеexport
, это формат, который обычно используется в нашей разработке. Хотя многие новые браузеры в настоящее время поддерживают<script type="module">
Теперь он поддерживает запуск прямо в браузере.ES6
код, но браузер не поддерживаетnode_modules
, так что наш оригиналES6
Код по-прежнему не может работать в браузере, поэтому я временно думаю, что браузер его не поддерживает.ES6
код, все еще нужно сделать преобразование.
В следующей таблице показан поддерживаемый диапазон каждого формата, а скобки указывают на то, что он должен поддерживаться внешними инструментами.
Node | браузер | |
---|---|---|
AMD | не поддерживается (require.js, r.js) | Не поддерживается (require.js) |
CommonJS | служба поддержки | Не поддерживается (webpack/browserify) |
ESModule | не поддерживается (бабель) | Не поддерживается (веб-пакет) |
Чтобы выполнять модульные тесты в разных средах, вам нужно набирать пакеты, соответствующие разным средам, поэтому при создании тестовой цепочки инструментов вы должны определить, в какой среде вы работаете.Node
Просто добавьте слойbabel
конверсия, если она в реальном браузере, ее нужно увеличитьwebpack
Шаги обработки.
Итак, чтобы иметь возможностьNode
относящийся к окружающей средеMocha
используется вES Module
Есть два способа
-
Node
Окружение изначально благосклонноES Module
(node version >= 15) - использовать
babel
код для преобразования
Первый метод пропускается, а второй метод использует следующую конфигурацию
npm install @babel/register @babel/core @babel/preset-env --save-dev
// .mocharc.js
+ require('@babel/register');
global.expect = require('chai').expect;
// .babelrc
+ {
+ "presets": ["@babel/preset-env" ,“@babel/preset-typescript”]
+ }
Аналогично, если использовать в проектеTypeScript
, вы можете использоватьts-node/register
решить, потому чтоTypeScript
родная поддержкаES Module
Перевести вCJS
, так что поддержитеTypeScript
тогда вам не нужно использоватьbabel
для преобразования. (Здесь предполагается, чтоTypeScript
конфигурация по умолчанию)
npm install ts-node typescript --save-dev
// .mocharc.js
require('ts-node/register');
Mocha
изначально поддерживает браузеры иNode
Для стороннего тестирования, чтобы протестировать на стороне браузера, нам нужно написать html, который использует<script src="mocha.min.js">
, а затем вставить все локальные файлы в html для завершения теста.Эффективность ручного проектирования относительно низка, поэтому для выполнения этой задачи необходимо использовать инструменты.Этот инструментKarma
.
Karma
По сути, запуск веб-сервера локально, затем запуск внешнего браузера для загрузки сценария начальной загрузки, который загружает все наши исходные и тестовые файлы в браузер, который в конечном итоге выполнит наши тесты в коде варианта использования на стороне браузера. так что используйтеKarma
+ mocha
+chai
Вы можете создать полную цепочку инструментов модульного тестирования на стороне браузера.
npm install karma mocha chai karma-mocha karma-chai --save-dev
npx karma init
// Which testing framework do you want to use: mocha
// Do you want to use Require.js: no
// Do you want capture any browsers automatically: Chrome
здесьKarma
выбрано при инициализацииMocha
поддержка, затем втораяRequire.js
Обычно нет, если только не используется в бизнес-кодеamd
тип упаковки. третий вариантChrome
в качестве тестового браузера. Затем настройте его отдельно в кодеchai
.
// karma.conf.js
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
- frameworks: ['mocha'],
+ frameworks: ['mocha', 'chai'],
// list of files / patterns to load in the browser
files: [],
Karma
изframeworks
Роль заключается в глобальном внедрении некоторых зависимостей.Mocha
а такжеchai
Предоставленные инструменты, связанные с тестированием, доступны глобально для использования в коде.Karma
Просто отправьте наш файл в браузер для выполнения, но, согласно вышеизложенному, наш код должен пройти черезwebpack
илиbrowserify
Он может работать только на стороне браузера после упаковки.
Если исходный код ужеCJS
, вы можете использоватьbrowserify
Для поддержки работы на стороне браузера, в основном нулевой настройки, но часто реальный мир более сложен, у нас естьES6
,JSX
так же какTypeScript
для обработки, поэтому здесь мы используемwebpack
.
Нижеwebpack
информация о конфигурации.
npm install karma-webpack@4 webpack@4 @babel/core @babel/preset-env @babel/preset-react babel-loader --save-dev
// karma.conf.js
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['mocha', 'chai'],
// list of files / patterns to load in the browser
files: [
+ { pattern: "test/*.test.js", watched: false }
],
preprocessors: {
+ 'test/**/*.js': [ 'webpack']
},
+ webpack: {
+ module: {
+ rules: [{
+ test: /.*\.js/,
+ use: 'babel-loader'
+ }]
+ }
+ },
// .babelrc
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
Здесь мы тестируемReact
Код программы следующий
// js/index.js
import React from 'react';
import ReactDOM from 'react-dom';
export function renderToPage(str) {
const container = document.createElement('div');
document.body.appendChild(container);
console.log('there is real browser');
return new Promise(resolve => {
ReactDOM.render(<div>{ str } </div>, container, resolve);
});
}
// test/index.test.js
import { renderToPage } from '../js/index';
describe('renderToPage', () => {
it ('should render to page', async function () {
let content = 'magic string';
await renderToPage(content);
expect(document.documentElement.innerText).to.be.contain(content);
})
})
и откройте локальный браузер
Вы можете видеть, что тестовая программа теперь работает в реальном браузере.
Потому что графические пары тестовCI
Машина не дружелюбная, поэтому есть выборpuppeteer
заменятьChrome
.
Кроме того, это очень тяжелые пакеты, если они не сильно зависят от реальных браузеров, их можно использовать.JSDOM
существуетNode
Клиент имитирует среду браузера.
Обобщить небольшую цепочку инструментов
- Цепочка инструментов тестирования в среде Node может быть:
mocha
+chai
+babel
- Смоделированная среда браузера может быть:
mocha
+chai
+babel
+jsdom
- Набор тестовых инструментов в реальной среде браузера может быть:
karma
+mocha
+chai
+webpack
+babel
Конвейер тестирования часто требует одновременного использования множества инструментов, что усложняет настройку, а также некоторых дополнительных инструментов, таких как модульное покрытие (Istanbul), моделирование функции/времени (sinon.js) и другие инструменты. Иногда согласование между инструментами может быть не идеальным, а выбор отнимает много времени и сил.
jasmine
Внешний вид .jasmine
Обеспечьте тестовую среду, которая включает в себя структуру процесса тестирования, функции утверждения, фиктивные инструменты и другие инструменты, которые будут встречаться при тестировании. можно аппроксимировать какjasmine = mocha + chai + 辅助工具
.
попробуй дальшеjasmine
рабочий процесс.
использоватьnpx jasmine init
После инициализации он будет сгенерирован в текущем каталогеspec
каталог, который содержит файл конфигурации по умолчанию
// ./spec/support/jasmine.json
{
"spec_dir": "spec",
"spec_files": [
"**/*[sS]pec.js"
],
"helpers": [
"helpers/**/*.js"
],
"stopSpecOnExpectationFailure": false,
"random": true
}
Если вы хотите загрузить некоторую глобальную конфигурацию, вы можетеspec/helpers
положить некоторые в каталогjs
файл, как сказано в конфигурации, jasmine будет выполняться при запускеspec/helpers
все в каталогеjs
документ.
Например, мы часто используемes6
грамматика, вам нужно добавитьes6
служба поддержки.
новыйspec/helpers/babel.js
Напишите следующую конфигурацию.
npm install @babel/register @babel/core @babel/preset-env --save-dev
// spec/helpers/babel.js
require('babel-register');
// .babelrc
{
"presets": ["@babel/preset-env"]
}
а такжеmocha
то же самое, если нужноTypeScript
поддержку, вы можете использовать следующую конфигурацию
npm install ts-node typescript --save-dev
// spec/helpers/typescript.js
require('ts-node/register');
в файле конфигурацииspec_dir
даjasmine
согласованный каталог файлов прецедентов,spec_files
Формат файла варианта использования указан какxxx.spec.js
.
С этой конфигурацией по умолчанию вы можете написать варианты использования по мере необходимости, например
// ./spec/index.spec.js
import { multiple } from '../index.js';
describe('Multiple', () => {
it ('should be a function', () => {
expect(multiple).toBeInstanceOf(Function);
})
it ('should 7 * 2 = 14', () => {
expect(multiple(7, 2)).toEqual(14);
})
it ('should 7 * -2 = -14', () => {
expect(multiple(7, -2)).toEqual(-14);
})
})
jasmine
Стиль утверждения иchai
Очень разные,jasmine
изAPI
следующим образом, сchai
Напишите намного меньше, чем.
, а поддерживаемые функции понятнее, без рассмотрения того, как их комбинировать, и далее описываетсяjest
Платформы тестирования также используют этот стиль.
nothing()
toBe(expected)
toBeCloseTo(expected, precisionopt)
toBeDefined()
toBeFalse()
toBeFalsy()
toBeGreaterThan(expected)
toBeGreaterThanOrEqual(expected)
toBeInstanceOf(expected)
toBeLessThan(expected)
toBeLessThanOrEqual(expected)
toBeNaN()
toBeNegativeInfinity()
toBeNull()
toBePositiveInfinity()
toBeTrue()
toBeTruthy()
toBeUndefined()
toContain(expected)
toEqual(expected)
toHaveBeenCalled()
toHaveBeenCalledBefore(expected)
toHaveBeenCalledOnceWith()
toHaveBeenCalledTimes(expected)
toHaveBeenCalledWith()
toHaveClass(expected)
toHaveSize(expected)
toMatch(expected)
toThrow(expectedopt)
toThrowError(expectedopt, messageopt)
toThrowMatching(predicate)
withContext(message) → {matchers}
бегатьjasmine
Отчет о тестировании может быть сгенерирован
Отчет о тестировании по умолчанию не очень интуитивно понятен, если вы хотите предоставить что-то вродеMocha
отчеты стиля могут быть установленыjasmine-spec-reporter
,существуетspec/helpers
Добавьте файл конфигурации в каталог, например.spec/helpers/reporter.js
.
const SpecReporter = require('jasmine-spec-reporter').SpecReporter;
jasmine.getEnv().clearReporters(); // remove default reporter logs
jasmine.getEnv().addReporter(new SpecReporter({ // add jasmine-spec-reporter
spec: {
displayPending: true
}
}));
Вывод отчета о вариантах использования в настоящее время выглядит следующим образом.
если вJasmine
Чтобы выполнить тестирование на уровне DOM вKarma
илиJSDOM
Конкретная конфигурация здесь повторяться не будет.
Подводить итогиJasmine
Набор инструментов
- Тест в среде Node:
Jasmine
+babel
- моделирование
JSDOM
тестовое задание :Jasmine
+JSDOM
+babel
- Реальный тест браузера:
Karma
+Jasmine
+webpack
+babel
JEST
Jest
даfacebook
Полное техническое решение для модульного тестирования, которое объединяет среду тестирования, библиотеку утверждений, средство запуска, моментальный снимок, песочницу и средство имитации.React
Официальный инструмент тестирования.Jest
а такжеJasmine
очень похожиAPI
, так что вJasmine
Инструменты, используемые вJest
все еще можно использовать естественным путем. можно аппроксимировать какJest = JSDOM 启动器 + Jasmine
.
Хотя Jest предоставляет множество функций, в нем нет встроенныхES6
Поддержка, поэтому код все еще нужно преобразовать в соответствии с разными средами выполнения, потому что Jest в основном работает вNode
, поэтому вам нужно использоватьbabel-jest
БудуES Module
Перевести вCommonJS
.
Конфигурация Jest по умолчанию
npm install jest --save-dev
npx jest --init
√ Would you like to use Jest when running "test" script in "package.json"? ... yes
√ Would you like to use Typescript for the configuration file? ... no
√ Choose the test environment that will be used for testing » jsdom (browser-like)
√ Do you want Jest to add coverage reports? ... no
√ Which provider should be used to instrument code for coverage? » babel
√ Automatically clear mock calls and instances between every test? ... yes
существуетNode
илиJSDOM
увеличить внизES6
поддержка кода
npm install jest-babel @babel/core @babel/preset-env
// .babelrc
{
"presets": ["@babel/preset-env"]
}
// jest.config.js
// 下面两行为默认配置,不写也可以
{
+ testEnvironment: "jsdom",
+ transform: {"\\.[jt]sx?$": "babel-jest"}
}
использоватьJest
Создать тестовый отчет
дляReact
а такжеTypeScript
Поддержка также может быть измененаbabel
Решение настройки
npm install @babel/preset-react @babel/preset-typescript --save-dev
// .babrlrc
{
"presets": ["@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"]
}
Jest протестирован в реальной среде браузера
В настоящее времяJest
Не поддерживает тестирование непосредственно в реальных браузерах, его пусковая установка по умолчанию предоставляет толькоJSDOM
среда, модульное тестирование в браузере в настоящее время толькоKarma
Схема может это сделать, значит, ее тоже можно использоватьKarma
+ Jest
Решение реализовано, но не рекомендуется, т.к.Jest
Он слишком тяжелый, используйтеKarma
+ Jasmine
можно добиться в основном такого же эффекта.
Есть и более популярныйE2E
строить планыJest
+ Puppeteer
, из-заE2E
Он не относится к категории юнит-тестирования и не будет здесь раскрываться.
Jest
Сводка цепочки инструментов
- Тест в среде Node:
Jest
+babel
-
JSDOM
тестовое задание :Jest
+babel
- Тестирование в реальном браузере (не рекомендуется)
-
E2E
тестовое задание :Jest
+Puppeteer
краткое изложение
Выше описанаchai
, mocha
, karma
, jasmine
а такжеjest
, Каждому инструменту соответствует своя уникальная цепочка инструментов. Выбирая подходящий инструмент тестирования, выбирайте его в соответствии с реальными потребностями. В области тестирования все еще есть много инструментов, которых слишком много, чтобы сосчитать. Давайте посмотрим на некоторые методы React модульное тестирование.
Модульное тестирование React с помощью Jest + Enzyme
Enzyme
Базовая конфигурация выглядит следующим образом:
npm install enzyme enzyme-adapter-react-16 jest-enzyme jest-environment-enzyme jest-canvas-mock react@16 react-dom@16 --save-dev
// jest.config.js
{
- "testEnvironment": "jsdom",
+ setupFilesAfterEnv: ["jest-enzyme", "jest-canvas-mock"],
+ testEnvironment: "enzyme",
+ testEnvironmentOptions: {
+ "enzymeAdapter": "react16"
+ },
}
jest-canvas-mock
Этот пакет предназначен для решения некоторых задачJSDOM
Проблема с нереализованным поведением, вызывающим предупреждения.
Вышеупомянутое строит использованиеEnzyme
Более дружелюбная среда, на которую можно напрямую ссылаться в глобальной области видимости.React
, shallow
, mount
ЖдатьAPI
. такжеEnzyme
Также зарегистрирован ряд дружественных функций утверждений дляJest
, как показано ниже,Справочный адрес
toBeChecked()
toBeDisabled()
toBeEmptyRender()
toExist()
toContainMatchingElement()
toContainMatchingElements()
toContainExactlyOneMatchingElement()
toContainReact()
toHaveClassName()
toHaveDisplayName()
toHaveHTML()
toHaveProp()
toHaveRef()
toHaveState()
toHaveStyle()
toHaveTagName()
toHaveText()
toIncludeText()
toHaveValue()
toMatchElement()
toMatchSelector()
// js/ClassComponent.js
import React from 'react';
export default class ClassComponent extends React.PureComponent {
constructor() {
super();
this.state = { name: 'classcomponent' };
}
render() {
return (
<div>
a simple class component
<CustomComponent />
</div>
);
}
}
// test/hook.test.js
import HookComponent from '../js/HookComponent';
describe('HookComponent', () => {
it ('test with shallow', () => {
const wrapper = shallow(<HookComponent id={1} />);
expect(wrapper).toHaveState('name', 'classcomponent');
expect(wrapper).toIncludeText('a simple class component');
expect(wrapper).toContainReact(<div>a simple class component</div>);
expect(wrapper).toContainMatchingElement('CustomComponent');
})
})
Enzyme
Предоставляет три метода компонента рендеринга
-
shallow
использоватьreact-test-renderer
Рендеринг компонентов в виде объектов в памяти может быть выполнен легко.props
,state
и другие тесты данных, соответствующие объекты операцииShallowWrapper
, в этом режиме может быть воспринят только первый слой пользовательских подкомпонентов, а внутренняя структура пользовательских подкомпонентов не может быть воспринята. -
mount
использоватьreact-dom
Рендеринг компонента создаст реальныйDOM
узел, чемshallow
По сравнению с увеличением, вы можете использовать роднойAPI
действоватьDOM
способность, соответствующий объект операцииReactWrapper
, то, что воспринимается в этом режиме, является полнымDOM
Дерево. -
render
использоватьreact-dom-server
визуализировать какhtml
Строка, основанная на этом статическом документе для работы, соответствующий объект операцииCheerioWrapper
.
Поверхностный рендеринг
потому чтоshallow
Шаблоны учитывают только первый уровень пользовательских подкомпонентов и часто используются только для простого тестирования компонентов. Например, следующие компоненты
// js/avatar.js
function Image({ src }) {
return <img src={src} />;
}
function Living({ children }) {
return <div className="icon-living"> { children } </div>;
}
function Avatar({ user, onClick }) {
const { living, avatarUrl } = user;
return (
<div className="container" onClick={onClick}>
<div className="wrapper">
<Living >
<div className="text"> 直播中 </div>
</Living>
</div>
<Image src={avatarUrl} />
</div>
)
}
export default Avatar;
shallow
Хотя рендеринг не является реальным рендерингом, жизненный цикл его компонентов будет проходить через него полностью.
использоватьshallow(<Avatar />)
Воспринимаемая структура выглядит следующим образом, обратите внимание, чтоdiv.text
так какLiving
компонентchildren
можно обнаружить, ноLiving
Внутренняя структура системы не может быть воспринята.
Enzyme
Поддерживаемые селекторы поддерживают наши знакомыеcss selector
синтаксис, и в этом случае мы можемDOM
Конструкция проверяется следующим образом.
// test/avatar.test.js
import Avatar from '../js/avatar';
describe('Avatar', () => {
let wrapper = null, avatarUrl = 'abc';
beforeEach(() => {
wrapper = shallow(<Avatar user={{ avatarUrl: avatarUrl }} />);
})
afterEach(() => {
wrapper.unmount();
jest.clearAllMocks();
})
it ('should render success', () => {
// wrapper 渲染不为空
expect(wrapper).not.toBeEmptyRender();
// Image 组件渲染不为空, 这里会执行 Image 组件的渲染函数
expect(wrapper.find('Image')).not.toBeEmptyRender();
// 包含一个节点
expect(wrapper).toContainMatchingElement('div.container');
// 包含一个自定义组件
expect(wrapper).toContainMatchingElement("Image");
expect(wrapper).toContainMatchingElement('Living');
// shallow 渲染不包含子组件的内部结构
expect(wrapper).not.toContainMatchingElement('img');
// shallow 渲染包含 children 节点
expect(wrapper).toContainMatchingElement('div.text');
// shallow 渲染可以对 children 节点内部结构做测试
expect(wrapper.find('div.text')).toIncludeText('直播中');
})
})
Если мы хотим протестировать соответствующий компонентprops
/ state
Его также можно легко протестировать, но в настоящее время есть дефекты.Class Component
может пройтиtoHaveProp
, toHaveState
прямой тест, ноHook
Компонент не может быть протестированuseState
.
it ('Image component receive props', () => {
const imageWrapper = wrapper.find('Image');、
// 对于 Hook 组件目前我们只能测试 props
expect(imageWrapper).toHaveProp('src', avatarUrl);
})
wrapper.find
вернет тот же самый, хотяShallowWrapper
объект, но подструктура этого объекта не расширяется, если вы хотите протестироватьimageWrapper
внутренняя структура, необходимо повторноshallow render
однажды.
it ('Image momponent receive props', () => {
const imageWrapper = wrapper.find('Image').shallow();
expect(imageWrapper).toHaveProp('src', avatarUrl);
expect(imageWrapper).toContainMatchingElement('img');
expect(imageWrapper.find('img')).toHaveProp('src', avatarUrl);
})
Вы также можете изменить компонентprops
, запускает перерисовку компонента
it ('should rerender when user change', () => {
const newAvatarUrl = '' + Math.random();
wrapper.setProps({ user: { avatarUrl: newAvatarUrl }});
wrapper.update();
expect(wrapper.find('Image')).toHaveProp('src', newAvatarUrl);
})
Другим распространенным сценарием является имитация события.Событие ближе к реальному тестовому сценарию.В этом сценарии используйтеshallow
Есть много недостатков, потому чтоshallow
События сцены не будут иметь того же процесса захвата и всплытия, что и реальные события, поэтому в настоящее время вы можете просто активировать соответствующийcallback
достичь цели тестирования.
it ('will call onClick prop when click event fired', () => {
const fn = jest.fn();
wrapper.setProps({ onClick: fn });
wrapper.update();
// 这里触发了两次点击事件,但是 onClick 只会被调用一次。
wrapper.find('div.container').simulate('click');
wrapper.find('div.wrapper').simulate('click');
expect(fn).toHaveBeenCalledTimes(1);
})
Некоторые люди в Интернете резюмировалиshallow
Некоторые недостатки режима
-
shallow
Рендеринг не пузыряет события, аmount
Могу. -
shallow
Рендеринг, потому что не создает реальногоDOM
, поэтому компонент используетrefs
нельзя нормально получить, если очень надо использоватьrefs
, вы должны использоватьmount
. -
simulate
существуетmount
было бы более полезным, потому что он делает всплытие событий.
Фактически, приведенные выше пункты иллюстрируют явление, котороеshallow
Часто подходит только для идеального сценария, некоторые операции зависят от поведения браузера.shallow
не могут быть удовлетворены, они, связанные с реальной средой, могут быть использованы толькоmount
.
Рендеринг монтирования
Mount
Визуализированная структура объектаReactWrapper
который обеспечивает иShallowWrapper
почти то же самоеAPI
, разница небольшая.
существуетAPI
Некоторые различия в уровнях заключаются в следующем.
+ getDOMNode() 获取DOM节点
+ detach() 卸载React组件,相当于 unmountComponentAtNode
+ mount() 挂载组件,unmount之后通过这个方法重新挂载
+ ref(refName) 获取 class component 的 instance.refs 上的属性
+ setProps(nextProps, callback)
- setProps(nextProps)
- shallow()
- dive()
- getElement()
- getElements()
Кроме того, из-заmount
использоватьReactDOM
Рендеринг, чтобы он был ближе к реальной сцене, в этом режиме мы можем наблюдать весьDOM
Структура и структура узла компонента React.
describe('Mount Avatar', () => {
let wrapper = null, avatarUrl = '123';
beforeEach(() => {
wrapper = mount(<Avatar user={{ avatarUrl }} />);
})
afterEach(() => {
jest.clearAllMocks();
})
it ('should set img src with avatarurl', () => {
expect(wrapper.find('Image')).toExist();
expect(wrapper.find('Image')).toHaveProp('src', avatarUrl);
expect(wrapper.find('img')).toHaveProp('src', avatarUrl);
})
})
существуетshallow
Проблема триггера события, которую нельзя смоделировать вmount
Вниз больше не проблема.
it ('will call onClick prop when click event fired', () => {
const fn = jest.fn();
wrapper.setProps({ onClick: fn });
wrapper.update();
wrapper.find('div.container').simulate('click');
wrapper.find('div.wrapper').simulate('click');
expect(fn).toHaveBeenCalledTimes(2);
})
В заключениеshallow
что может быть сделаноmount
сможет сделать,mount
что может быть сделаноshallow
Не обязательно.
Оказывать
render
внутреннее использованиеreact-dom-server
Отобразите в строку, а затем пройдите черезCherrio
Преобразовать в структуру в памяти, вернутьCheerioWrapper
экземпляр, способный отобразить весьDOM
дерево, но потеряет состояние внутреннего экземпляра, поэтому его также называютStatic Rendering
. Этот тип рендеринга может выполнять относительно немного операций, и здесь он не будет подробно описываться.официальная документация.
Суммировать
Для реальных браузеров я бы рекомендовалKarma
+ Jasmine
Сценарное тестирование, дляReact
тестовое заданиеJest
+ Enzyme
существуетJSDOM
Окружающая среда смогла охватить большинство сцен. дополнительный тестReact
компоненты, кромеEnzyme
предоставленные операции,Jest
Есть много других полезных функций вmock
Одинnpm
Реализация компонента, настройкаsetTimeout
Часы и т. д., эти инструменты также незаменимы при выполнении юнит-тестирования.Вся система технологии юнит-тестирования содержит много всего.Эта статья не может охватить все, а лишь знакомит с некоторыми родственными технологическими системами, наиболее близкими нам.
Ссылаться на
- medium.com/building-IB…
- medium.com/@внезапно крик.oz/…
- Ууху. Обратите внимание. Talent/2015/10/12/…
- jestjs.io/docs/en
- blog.bit SRC.IO/как протестировать…
- woohoo.бесплатный код camp.org/news/special Fitness…
- вместе.
Эта статья была опубликована сКоманда внешнего интерфейса NetEase Cloud Music, Любое несанкционированное воспроизведение статьи запрещено. Мы набираем front-end, iOS и Android круглый год.Если вы готовы сменить работу и любите облачную музыку, присоединяйтесь к нам на grp.music-fe(at)corp.netease.com!