какБогатая документацияИзвестный фреймворк для фронтенд-разработки,Vue.jsОфициальная документация по компонентам Vue знакомит с методами модульного тестирования компонентов Vue в разделах «Учебное пособие — Инструменты — Модульное тестирование» и «Поваренная книга — Модульное тестирование компонентов Vue», а также предоставляет официальную служебную библиотеку модульного тестирования.Vue Test Utils; даже в инструментах управления состояниемVuexГлава «Тестирование» не забыта в документации.
Именно поэтому команда разработчиков Vue.js уделяет так много внимания модульному тестированию.Легко начатьАктивно популяризировать науку в рамках торговых точек?
Официальная документация дает очень четкое заявление:
组件的单元测试有很多好处:
- 提供描述组件行为的文档
- 节省手动测试的时间
- 减少研发新特性时产生的 bug
- 改进设计
- 促进重构
自动化测试使得大团队中的开发者可以维护复杂的基础代码。
Эта статья как«Модульное тестирование компонентов React»В статье, дополняющей эту статью, я попытаюсь аналогичным образом обратиться к начинающим и средним разработчикам и сосредоточусь на модульном тестировании вСтек технологий Vue.jsВведение в приложения в.
I. Введение в модульное тестирование
Модульное тестирование относится к проверке и проверке наименьшей тестируемой единицы программного обеспечения.
Проще говоря,单元
Это самый маленький протестированный функциональный модуль, созданный людьми. Модульное тестирование — это самый низкий уровень тестирования, который должен выполняться во время разработки программного обеспечения, когда отдельные модули программного обеспечения тестируются изолированно от остальной части программы.
Для различных тестов в деятельности по разработке приведенная выше диаграмма является одним из наиболее распространенных делений: снизу вверх,Модульное тестирование -> Интеграционное тестирование -> Сквозное тестирование, с увеличением степени его интеграции снижается соответствующая степень автоматизации.
Цикл обратной связи и исправления сквозного (тестирование, которое проходит через функции в реальных сценариях, таких как браузеры, и обращение с программой как черным ящиком) и интеграционное тестирование (собирание нескольких протестированных модулей для совместного тестирования) является относительно длительным, а выполнение скорость медленная Тестовая операция нестабильна, а стоимость обслуживания также высока, поскольку люди часто выполняют ее вручную. Модульный тест предназначен только для определенного метода или API, с точным позиционированием, с использованием механизма имитации, а скорость работы очень высока (уровень миллисекунд), и он выполняется локально разработчиком, а обратная связь и ремонт своевременны и стоимость низкая.
мы кладемподавляющее большинствоВарианты использования, которые могут быть охвачены модульными тестами, покрываются модульными тестами, и только те, которые не могут быть протестированы модульными тестами, будут покрыты сквозными и интеграционными тестами.
Прежде чем объяснять конкретные концепции модульного тестирования, сначалажевать каштанИнтуитивное понимание:
Например, у нас есть модуль, который предоставляет два метода для некоторой обработки пути меню:
// src/menuChecker.js
export function getRoutePath(str) {
let to = ""
//...
return to;
}
export function getHighlight(str) {
let hl = "";
//...
return hl;
}
Напишите соответствующий тестовый файл:
import {
getRoutePath,
getHighlight
} from "@/menuChecker";
describe("检查菜单路径相关函数", ()=>{
it("应该获得正确高亮值", ()=>{
expect( getHighlight("/myworksheet/(.*)") ).toBe("myTickets");
});
it("应该为未知路径取得默认的高亮值", ()=>{
expect( getHighlight("/myworksheet/ccc/aaa") ).toBe("mydefaulthl111");
});
it("应该补齐开头的斜杠", ()=>{
expect( getRoutePath("/worksheet/list") ).toBe('/worksheet/list');
});
it("应该能修正非法的路径", ()=>{
expect( getRoutePath("/myworksheet/(.*)") ).toBe("/myworksheet/list");
});
});
Запуск тестового файла дает следующий результат:
Результаты бега можно назвать очень дружелюбными, хотя яркие подсказкиFAIL, но какое суждение неверно, какая строка неверна, разница между фактическим возвращаемым значением и ожидаемым значением и даже таблица покрытия кода отображаются отдельно; особенно наиболее важные правильные и неправильные результаты отображаются зеленым и красным цветом соответственно. .
Истина одна, либо целевой модуль написан неправильно, либо условие теста написано неправильно — короче, исправляем и перезапускаем:
Отсюда у нас есть базовое понимание процесса модульного тестирования.
Во-первых, определение так называемого «модуля» является гибким, это может быть функция, модуль или компонент Vue.
Во-вторых, поскольку в результатах тестирования успешные варианты использования будут показаны зеленым цветом, а неудачные части — красным, поэтому модульное тестирование часто называют «красно-зеленым тестированием» или «красно-зеленым рефакторингом». общие шаги можно резюмировать следующим образом:
- добавить тест
- Запустите все тесты, чтобы убедиться, что только что добавленный тест завершился неудачей; если он прошел успешно, повторите шаг 1.
- В соответствии с отчетом об ошибках напишите или перепишите код целенаправленно; единственная цель этого шага — пройти тест, вам не нужно сначала беспокоиться о деталях.
- Запустите тест еще раз; в случае успеха перейдите к шагу 5, в противном случае повторите шаг 3.
- Рефакторинг кода, который прошел тесты, чтобы сделать его более читабельным и удобным в сопровождении, не влияя на прохождение тестов.
- Повторяйте шаг 1, пока не будут проверены все функции.
1.1 Платформа тестирования
Роль среды тестирования заключается в предоставлении удобного синтаксиса для описания тестовых случаев и группировки тестовых случаев.
1.2 Утверждения
Утверждения являются основной частью среды модульного тестирования, и ошибка утверждения приведет к сбою теста или сообщит об ошибке.
Ниже приведены некоторые примеры общих утверждений:
-
Равенство утверждает
- expect(sth).toEqual(value)
- expect(sth).not.toEqual(value)
-
Сравнение утверждает
- expect(sth).toBeGreaterThan(number)
- expect(sth).toBeLessThanOrEqual(number)
-
Тип утверждает
- expect(sth).toBeInstanceOf(Class)
-
Проверка состояния
- expect(sth).toBeTruthy()
- expect(sth).toBeFalsy()
- expect(sth).toBeDefined()
1.3 Библиотека утверждений
Библиотека утверждений в основном предоставляет семантические методы для вышеуказанных утверждений, которые используются для вынесения различных суждений о значениях, задействованных в тесте. Эти семантические методы возвращают результат теста, успешный или неуспешный. Общие библиотеки утверждений включают Should.js, Chai.js и т. д.
1.4 тестовый пример
Набор тестовых входных данных, условий выполнения и ожидаемых результатов, написанных для определенной цели, чтобы протестировать путь программы или убедиться, что определенное требование выполнено.
Общая форма:
it('should ...', function() {
...
expect(sth).toEqual(sth);
});
1.5 набор тестов набор тестов
Набор связанных тестов часто называют набором тестов.
Общая форма:
describe('test ...', function() {
it('should ...', function() { ... });
it('should ...', function() { ... });
...
});
1.6 spy
так как
spy
Буквально, мы используем этого «шпиона» для «контроля» за вызовом функции.
Упаковав функцию мониторинга, вы можете четко знать, по каким параметрам функция вызывается несколько раз, и каков результат возврата или даже выбрасывания аномалий.
var spy = sinon.spy(MyComp.prototype, 'someMethod');
...
expect(spy.callCount).toEqual(1);
1.7 stub
иногда используется
stub
Для встраивания или прямой замены некоторого кода для достижения цели изоляции
Одинstub
Этот модульный тест можно смоделировать с помощью подхода с минимальной зависимостью. Например, метод может зависеть от выполнения другого метода, который для нас прозрачен. Хорошей практикой является изолировать его заглушкой. Это обеспечивает более точное модульное тестирование.
var myObj = {
prop: function() {
return 'foo';
}
};
sinon.stub(myObj, 'prop').callsFake(function() {
return 'bar';
});
myObj.prop(); // 'bar'
1.8 mock
mock
Обычно это относится к методу тестирования, который использует виртуальный объект для создания метода тестирования для тестирования некоторых объектов, которые нелегко создать или получить в процессе тестирования.
Вообще говоря, вышеупомянутые шпион и заглушка, а также некоторые симуляции модулей, симуляции возвращаемых значений ajax и симуляции таймеров называются макетами.
1.9 Тестовое покрытие (покрытие кода)
Он используется для подсчета проверки кода тест-кейсами и формирования соответствующих отчетов, таких какistanbul
Это распространенный статистический инструмент тестового покрытия.
istanbul
Это столица Турции «Стамбул», названная так потому, что турецкие ковры известны во всем мире, а ковры используются для «покрытия» 😷.
Просмотрите график выше:
Колонки со 2 по 5 в таблице соответствуют четырем измерениям:
- Покрытие операторов: было ли выполнено каждое выражение
- охват филиала: каждый ли
if
блоки кода выполняются - покрытие функций: вызывается ли каждая функция
- Покрытие строки: выполняется ли каждая строка
Результаты теста делятся на три типа: «зеленый», «желтый» и «красный» в зависимости от покрытия.На эти показатели следует обращать внимание.Чем полнее тест, тем выше можно обеспечить уверенность.
Также нет необходимости слепо следовать линейному покрытию, потому что это заставит нас слишком много внимания уделять деталям внутренней реализации компонента, что приведет к тривиальным тестам.
2. Инструменты модульного тестирования в Vue.js
2.1 Jest
Отличие от «традиционных» (на самом деле, не появлявшихся уже несколько лет) фреймворков для фронтенд-тестирования, таких как jasmine/Mocha/Chai;Jest
проще в использовании (возможно, исходное значение слова «шутки, шутки»), обеспечивает более высокую степень интеграции и более богатые функции.
Jest — это средство запуска тестов, разработанное Facebook.По сравнению с другими средами тестирования, Jest характеризуется встроенными общими инструментами тестирования, такими как встроенные инструменты утверждения и покрытия тестами, которые можно использовать «из коробки».
Кроме того, тестовые случаи Jest выполняются параллельно, и выполняются только тесты, соответствующие измененным файлам, что повышает скорость тестирования.
настроить
Jest утверждает, что является «платформой для тестирования нулевой конфигурации», простоnpm scripts
настроен внутриtest: jest
, Вы можете запуститьnpm test
, автоматически определять и тестировать те, которые соответствуют его правилам (обычно в проектах Vue.js__tests__
каталог) использовать файлы прецедентов.
В реальном использовании, если вы настроите конфигурацию в поле jest файла package.json или в независимом файле jest.config.js, вы получите более подходящий тестовый сценарий.
Справочная документацияv UE-test-u для LS.v UE JS.org/this/guides/he…Вскоре тестовую среду Jest можно будет настроить в проекте Vue.js.
четыре основных слова
Синтаксис написания модульных тестов обычно очень прост.jest
Например, поскольку его внутреннее использованиеJasmine 2
для тестирования, поэтому его синтаксис варианта использования такой же, как у Jasmine.
На самом деле, просто заучить эти четыре слова достаточно для большинства тестовых ситуаций:
-
describe
: определить набор тестов -
it
: определить тестовый пример -
expect
: условие суждения утверждения -
toEqual
: результат сравнения утверждения
describe('test ...', function() {
it('should ...', function() {
expect(sth).toEqual(sth);
expect(sth.length).toEqual(1);
expect(sth > oth).toEqual(true);
});
});
2.2 sinon
"Я веду коня" на картине - это не генерал Ша Вуцзин... На самом деле сюжет в картине - это всем известный "Троянский конь"; вероятно, это означает, что греки осаждали троянцев более десяти лет, не имея возможности атаковать долгое время, он решил отвести лагерь, оставив только огромного троянского коня (с солдатами в нем) и этого человека, достаточно раздетого и избитого, о котором здесь пойдет речь. обманул им троянцев --- всем известен сюжет.
Таким образом, этот названный инструмент тестирования также представляет собой набор различных методов маскировки проникновения, предоставляя независимые и богатые методы шпионажа, заглушки и имитации для модульного тестирования, совместимые с различными средами тестирования.
Хотя в самом Jest есть некоторые средства реализации шпионажа и т. д., sinon удобнее в использовании.
2.3 Vue Test Utils
Vue Test Utils — официальная служебная библиотека модульного тестирования для Vue.js; служебная библиотека очень похожа на служебную библиотеку Enzyme для тестирования компонентов React.
Он имитирует часть похожего на jQuery API, который очень интуитивно понятен и прост в использовании и освоении.Он предоставляет некоторые интерфейсы и несколько методов для сокращения стандартного кода тестирования, облегчает оценку, манипулирование и обход вывода Vue Component, а также сокращает код тестирования и реализацию.Связь между кодами.
обычно используют егоmount()
илиshallowMount()
способ преобразования целевого компонента вWrapper
object и вызовите его различные методы в тесте, такие как:
import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'
describe('Foo', () => {
it('renders a div', () => {
const wrapper = mount(Foo)
expect(wrapper.contains('div')).toBe(true)
})
})
3. Пример модульного теста Vue.js
3.1 Еще один каштан
import { shallowMount } from "@vue/test-utils";
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import i18nMessage from '@/i18n';
import Comp from "@/components/Device.vue";
const fakeData = { //假数据
deviceNo: "abcdefg",
deviceSpace: 45,
deviceStatus: 2,
devices: [
{
id: "test001",
location: "12",
status: 1
},
{
id: "test002",
location: "58",
status: 3
},
{
id: "test003",
location: "199",
status: 4
}
]
};
Vue.use(VueI18n); //重现必要的依赖
const i18n = new VueI18n({
locale: 'zh-CN',
silentTranslationWarn: true,
missing: (locale, key, vm) => key,
messages: i18nMessage
});
let wrapper = null;
const makeWrapper = ()=>{
wrapper = shallowMount( Comp, {
i18n, //看这里
propsData: { //还有这里
unitHeight: 5,
data: fakeData
}
} );
};
afterEach(()=>{ //也很常见的用法
if (!wrapper) return;
wrapper = null;
});
describe("test Device.vue", ()=>{
it("should be a VUE instance", ()=>{
makeWrapper();
expect( wrapper.isVueInstance() ).toBeTruthy();
});
it("应该有正常的总高度", ()=>{
makeWrapper();
expect( wrapper.vm.totalHeight ).toBe( 1230 );
});
it("应该渲染正确的设备数量", ()=>{
makeWrapper();
expect( wrapper.findAll('.deviceitem').length ).toBe( 3 );
});
it("指定的设备应该在正确的位置", ()=>{
makeWrapper();
const sty = wrapper.findAll('.deviceitem').at(1).attributes('style');
expect( sty ).toMatch( /height\:\s*20px/ );
expect( sty ).toMatch( /bottom\:\s*20px/ );
});
it("应该渲染正确的tooltip", ()=>{
makeWrapper();
//这里的用法值得注意
const popper_ref = wrapper.find({ref: 'device_tooltip_test002'});
expect( popper_ref.exists() ).toBeTruthy();
const cont = popper_ref.find('.tooltip_cont');
expect( cont.html() ).toMatch(/所在位置\:\s58/);
});
it("应该渲染正确的设备分类", ()=>{
makeWrapper();
const badge = wrapper.find('.badge');
expect( badge.exists() ).toBeTruthy();
expect( badge.findAll('li').length ).toBe(4);
expect( badge.findAll('li').at(2).text() ).toBe('喷雾设备');
});
it("当点击了关闭按钮,应该不再显示", (done)=>{ //异步的用例
makeWrapper();
wrapper.vm.$nextTick(()=>{ //再看这里
expect(
wrapper.find('.devices_container').exists()
).toBeFalsy();
done();
});
});
});
Здесь нет необходимости в пошаговых пояснениях, основной API находится вJest
а такжеVue Test Utils
можно найти в документации.
Среди них стоит отметить небольшой опыт: во-первых, некоторые асинхронные обновления (например, задержки в коде) используются корректно послеwrapper.vm.$nextTick
; Во-вторых, для некоторых элементов компонентов, смонтированных во внешних местах, таких как document.body, полагайтесь наwrapper.find({ref: xxx})
Получите его цитату.
3.2 Интеграция в рабочий процесс
Хорошо написанные модульные тесты, если вы только полагаетесь на каждогоnpm test
Ручное выполнение неизбежно приведет к тому, что со временем все забудут, постепенно устареют и, в конце концов, не смогут выполнить.
Существует несколько моментов времени, которые можно выбрать для вставки автоматического выполнения модульных тестов — например, каждый раз, когда файл сохраняется, каждый раз, когда выполняется сборка и т. д. Здесь мы выбрали очень простой метод настройки:
Первая установка в проектеpre-commit
пакет зависимостей; затем вpackage.json
Настройте сценарии npm в:
"scripts": {
...
"test": "jest"
},
"pre-commit": [
"test"
],
так каждый разgit commit
Раньше существовавшие в проекте юнит-тесты автоматически выполнялись один раз, чего часто избегали.«Измени одну ошибку, отправь десять новых ошибок»затруднительное положение.
IV. Улучшение компонентов Vue.js с помощью модульного тестирования
Помимо уменьшения количества ошибок, еще одним важным преимуществом модульного тестирования является то, что оно может сделать наши идеи компонентизации более ясными и качественными, а также выработать все более полезные привычки.
Компонент, который был проверен для отображения ожидаемого вывода для данного ввода, называетсяпрошел тесткомпоненты;
ОдинпроверяемыйКомпоненты означают, что их легко тестировать
Как убедиться, что компонент работает должным образом?
Возможно, мы привыкли полагаться на свои руки и глаза, чтобы проверять написанные нами компоненты снова и снова, но если вы планируете вручную проверять каждое изменение в каждом компоненте, рано или поздно, из-за усталости или лени, дефекты останутся. код.
Вот почему автоматизированное модульное тестирование важно. Модульные тесты гарантируют, что компоненты работают правильно каждый раз, когда в них вносятся изменения.
Тестирование подразделения не только о нахождении ошибок рано. Другим важным аспектом является его способность проверить качество архитектурного уровня компонента.
Одинне могу проверитьилитрудно проверитькомпоненты, в основном эквивалентныеплохо спроектированныйс компонент.
Компоненты трудно тестировать, потому что у них слишком много свойств, зависимостей, моделей, на которые ссылаются, и доступ к глобальным переменным — все это признаки плохого дизайна.
Плохо спроектированный компонент становится нетестируемым, и вы просто пропускаете модульные тесты, оставляя его непроверенным в порочном круге.
4.1 Надежда — последний каштан
Предположим, вы хотите протестировать компонент NumStepper.vue.
//NumStepper.vue
<template>
<div>
<button class="plus" v-on:click="updateNumber(+1)">加</button>
<button class="minus" v-on:click="updateNumber(-1)">减</button>
<button class="zero" v-on:click="clear">清</button>
</div>
</template>
<script>
export default {
props: {
targetData: Object,
clear: Function
},
methods: {
updateNumber: function(n) {
this.targetData.num += n;
}
}
}
</script>
Этот компонент, в свою очередь, полагается на внешний компонент, который предоставляет ему данные и методы:
//NumberDisplay.vue
<template>
<div>
<p>{{somedata.num}}</p>
<NumStepper :targetData="somedata" :clear="clear" />
</div>
</template>
<script>
import NumStepper from "./NumStepper"
export default {
components: {
NumStepper
},
data() {
return {
somedata: {
num: 999
},
tgt: this
}
},
methods: {
clear: function() {
this.somedata.num = 0;
}
}
}
</script>
Итак, наш тест должен быть написан так:
import { shallowMount } from "@vue/test-utils";
import Vue from 'vue';
import NumStepper from '@/components/NumStepper';
import NumberDisplay from '@/components/NumberDisplay';
describe("测试 NumStepper 组件", ()=>{
it("应该能够影响外层组件的数据", ()=>{
const display = shallowMount(NumberDisplay);
const wrapper = shallowMount(NumStepper, {
propsData: {
targetData: display.vm.somedata,
clear: display.vm.clear
}
});
expect(display.vm.somedata.num).toBe(999);
wrapper.find('.plus').trigger('click');
wrapper.find('.plus').trigger('click');
expect(display.vm.somedata.num).toBe(1001);
wrapper.find('.minus').trigger('click');
expect(display.vm.somedata.num).toBe(1000);
wrapper.find('.zero').trigger('click');
expect(display.vm.somedata.num).toBe(0);
})
});
<NumStepper>
Его очень сложно тестировать, потому что он привязан к деталям реализации внешних компонентов.
Для тестового сценария требуется дополнительный<NumberDisplay>
Компоненты, которые воспроизводят внешние компоненты, передают данные и методы целевому компоненту и проверяют, правильно ли целевой компонент изменяет состояние внешнего компонента.
Нетрудно представить, что если<NumberDisplay>
Когда компоненты зависят от других компонентов или переменных среды, глобальных методов и т. д., ситуация ухудшится, и некоторые специфичные для теста компоненты могут быть реализованы отдельно или даже вообще невозможно протестировать.
4.2 Настоящий последний каштан
когда<NumStepper>
Тестирование просто, когда оно не зависит от деталей внешних компонентов. Давайте реализуем и протестируем разумно упакованную версию<NumStepper>
Компоненты:
//NumStepper2.vue
<template>
<div>
<button class="plus" v-on:click="updateFunc(+1)">加</button>
<button class="minus" v-on:click="updateFunc(-1)">减</button>
<button class="zero" v-on:click="clearFunc">清</button>
</div>
</template>
<script>
export default {
props: {
updateFunc: Function,
clearFunc: Function
}
}
</script>
В испытаниях нет необходимости вводить дополнительные компоненты:
import { shallowMount } from "@vue/test-utils";
import Vue from 'vue';
import NumStepper from '@/components/NumStepper2';
describe("测试 NumStepper 组件", ()=>{
it("应该能够影响外层组件的数据", ()=>{
const obj = {
func1: function(){},
func2: function(){}
};
const spy1 = jest.spyOn(obj, "func1");
const spy2 = jest.spyOn(obj, "func2");
const wrapper = shallowMount(NumStepper, {
propsData: {
updateFunc: spy1,
clearFunc: spy2
}
});
wrapper.find('.plus').trigger('click');
expect(spy1).toHaveBeenCalled();
wrapper.find('.minus').trigger('click');
expect(spy1).toHaveBeenCalled();
wrapper.find('.zero').trigger('click');
expect(spy2).toHaveBeenCalled();
})
});
Примечание. В этом примере он только проверяет, был ли он нажат, и вы также можете ввести связанные методы sinon для проверки входящих параметров и т. Д. И написать более полный тест.
V. Резюме
В качестве классического метода разработки и рефакторинга модульное тестирование широко признано и применяется в области разработки программного обеспечения; фронтенд-область постепенно накапливает богатые среды и методы тестирования.
Модульное тестирование может обеспечить базовую гарантию нашей разработки и обслуживания, чтобы мы могли завершить создание и рефакторинг кода с ясным умом и практическим результатом.
Хорошая упаковка облегчает тестирование, тогда как неправильная упаковка затрудняет тестирование.
Тестируемость — это стандарт практики для проверки того, насколько хорошо структурирован компонент.
VI. Ссылки
- nuggets.capable/post/684490…
- nuggets.capable/post/684490…
- blog.CSDN.net/Новое странное прослушивание/Ах…
- woohoo.brief.com/afraid/ec40734 с 8…
- woohoooo.brief.com/afraid/rich 6 из 319 раундов…