Автор: Цзян Миньси, инженер-разработчик Beichao.
Эта статья также была опубликована вличный блог
Почему юнит-тест?
Статус проекта
Проекты, за которые я сейчас отвечаю в компании, можно разделить на две категории:
-
Один из них — это проект с высокой степенью сходства, например, управленческий фон.Страницы этого типа проекта проходят через различныеобщедоступный компонентпостроен. Возможность повторного использования общедоступных компонентов очень высока, поэтому качество особенно важно. Если разработчики оставят ошибки после изменения общих компонентов, это напрямую ухудшит качество всего проекта. Я хочу, чтобы программа тестировала эти общедоступные компоненты, чтобы убедиться, что каждый общедоступный компонент доступен.
-
Другая категория — основные проекты компании, для которых характерныдлительный цикл обслуживания, и будет продолжать добавлять новые функции. В процессе итерации версии проекта, когда в некоторых старых функциях, прошедших тестирование, есть ошибки, они могут быть обнаружены тестировщиками только на этапе тестирования. Я надеюсь, что программа сможет обеспечить нормальную работу некоторых функций ядра.Когда возникает ошибка в функции ядра, ее можно быстро обнаружить, а не обнаружить на этапе тестирования.
Чтобы решить вышеуказанную проблему, я пытаюсь представитьмодульный тест.
Роль модульного тестирования
-
Снижайте вероятность ошибок, быстро обнаруживайте ошибки и сокращайте повторяющееся ручное тестирование.
-
Улучшите качество кода и повысьте удобство сопровождения кода в проекте.
-
Для облегчения передачи проекта тестовый сценарий является лучшим описанием требований.
Далее поговорим о том, как проводить модульное тестирование.
Создайте тестовую среду
Краткий обзор инструментов тестирования
Mocha
Mocha (произносится как «Mocha») появился на свет в 2011 году и является одной из самых популярных сред тестирования JavaScript, доступной как в браузере, так и в среде Node.
Karma
Karma — это инструмент тестирования, разработанный командой Google. Это не среда тестирования, а драйвер для запуска тестов. Вы можете интегрировать свои любимые среды, библиотеки утверждений и браузеры через файлы конфигурации karma.
Vue Test Utils
официальныйсреда модульного тестирования, который предоставляет набор очень удобных инструментов, облегчающих написание модульных тестов для приложений Vue. Существует множество основных средств запуска тестов JavaScript, но Vue Test Utils поддерживает их все. Это тест-бегун агностик.
Библиотека утверждений Чай
Chai — это библиотека утверждений для Node и браузеров, которую можно комбинировать с любой средой тестирования JavaScript.
Метод сборки:
Тестовая среда, выбранная в этой статье, соответствует Karma + Mocha + Chai + Vue Test Utils.Процесс ручной настройки относительно громоздкий.Настоятельно рекомендуется использовать vue-cli.vue-cli имеет готовые шаблоны для создания проектов и выполните vue init webpack [название проекта], выберите «Karma + Mocha», когда «Выберите тестовый бегун» . vue-cli автоматически сгенерирует конфигурацию Karma + Mocha + Chai, нам нужно только дополнительно установить Vue Test Utils и выполнить npm install @vue/test-utils.
Если вы хотите настроить его самостоятельно, вы можете обратиться к этомустатья.
После завершения настройки на следующем рисунке показана структура каталогов проекта:
Под тестовой папкой находится папка модуля, которая содержит файлы, связанные с модульным тестированием.
specsХранится в тестовом скрипте, эта часть написана разработчиком.
coverageОтчет о тестировании хранится в папке, и вы можете визуально увидеть покрытие кода тестом, открыв внутри index.html.
Karma.conf.jsфайл конфигурации кармы.
Как писать тесты подразделения
Например
тестируемый компонентHelloWorld.vue(путь: E:\study\demo\src\components)
код показывает, как показано ниже:
<template>
<div class="hello">
<h1>Welcome to Your Vue.js App</h1>
</div>
</template>
тестовый скриптHelloWorld.spec.js(путь: E:\study\demo\test\unit\specs)
код показывает, как показано ниже:
import HelloWorld from '@/components/HelloWorld';
import { mount, createLocalVue, shallowMount } from '@vue/test-utils'
describe('HelloWorld.vue', () => {
it('should render correct contents', () => {
const wrapper = shallowMount(HelloWorld);
let content = wrapper.vm.$el.querySelector('.hello h1').textContent;
expect(content).to.equal('Welcome to Your Vue.js App');
});
});
1. Написание тестового сценария
describeпредставляет собой «набор тестов» и представляет собой набор связанных тестов. Это функция, первый аргумент которой — название набора тестов («тесты для функции сложения»), а второй аргумент — функция, которая фактически выполняется.
itПредставляет собой «тестовый пример» (test case), который представляет собой отдельный тест и является наименьшей единицей тестирования. Это тоже функция, первый параметр — это имя теста, а второй параметр — функция, которая фактически выполняется.
2. Использование библиотеки утверждений
В приведенном выше тестовом сценарии есть утверждение:
expect(content).to.equal('Welcome to Your Vue.js App');
Так называемое «утверждение» предназначено для оценки того, согласуется ли фактический результат выполнения исходного кода с ожидаемым результатом, и если он несовместим, выдается ошибка. Утверждение выше означает, что содержимое переменной должно быть равно «Добро пожаловать в ваше приложение Vue.js».
Все тестовые примеры (блоки) должны содержать одно или несколько утверждений. Это ключ к написанию тестовых случаев.
3. Просмотр результатов теста
Наконец, запустите модуль запуска npm, чтобы увидеть результаты:
Результаты показывают, что тест пройден.Откройте index.vue в разделе покрытия, чтобы просмотреть покрытие кода:
Поскольку это недавно созданный проект и код очень простой, покрытие составляет 100%. Покрытие кода — это объективные данные, которые не могут полностью отражать ситуацию с тестированием проекта, но имеют хорошее справочное значение. В командах разработчиков, состоящих из нескольких человек, охват может использоваться как жесткий критерий.
Это простой процесс написания модульного теста, не так ли? Давайте все попробуем сами.
дружеское напоминание
1. Установите плагин с помощью createLocalVue
Когда мы пишем модульные тесты для реальных проектов, код проекта будет намного сложнее, чем приведенные выше демонстрационные компоненты. Если вы используете vue-router или Vuex в одном компоненте, который хотите протестировать, используйте createLocalVue. Например, есть такой кусок кода:
data() {
return {
brandId: this.$route.query.id,
}
}
Объект $route необходимо внедрить в маршрутизатор с помощью createLocalVue, иначе при выполнении тестового скрипта возникнет ошибка. Используйте createLocalVue для решения этой проблемы, конкретный код:
import { shallowMount, createLocalVue } from '@vue/test-utils'
import VueRouter from 'vue-router'
const localVue = createLocalVue()
localVue.use(VueRouter)
const router = new VueRouter()
shallowMount(Component, {
localVue,
router
})
То же самое и с Vuex, я не буду вдаваться в подробности использования createLocalVue, вы можете прочитать официальную документацию.
2. Как сделать nextTick
Если вам нужно использовать nextTick в ваших собственных тестовых файлах, имейте в виду, что любые ошибки, возникающие внутри него, могут не быть обнаружены исполнителем тестов, потому что он использует промисы для внутреннего использования. Есть два предложения по этому вопросу: либо вы можете установить глобальный обработчик ошибок Vue на обратный вызов done в начале теста, либо вы можете вызвать nextTick без аргументов, чтобы вернуть его как обещание:
// 这不会被捕获
it('will time out', (done) => {
Vue.nextTick(() => {
expect(true).toBe(false)
done()
})
})
// 接下来的两项测试都会如预期工作
it('will catch the error using done', (done) => {
Vue.config.errorHandler = done
Vue.nextTick(() => {
expect(true).toBe(false)
done()
})
})
it('will catch the error using a promise', () => {
return Vue.nextTick()
.then(function () {
expect(true).toBe(false)
})
})
В реальном бою проекта ниже есть примеры использования nextTick, которые вы можете использовать в качестве справки.
3. Измените тестовый браузер по умолчанию.
Тест находится в конфигурационном файле karma.conf, браузеры по умолчанию 'PhantomJS'.
module.exports = function karmaConfig (config) {
config.set({
// browsers: ['PhantomJS'],
browsers: ['Chrome'],
Но я обнаружил, что предупреждения и подсказки об ошибках в среде PhantomJS отличаются от подсказок, которые я обычно вижу в браузере Chrome, и их немного сложно понять, как показано на рисунке:
Хром:
ФантомJS:Браузеры настроены на «Chrome», полученное сообщение об ошибке такое же, как и в реальном браузере Chrome, и можно использовать console.log(), а опыт отладки такой же, как и реальный опыт разработки. Единственным недостатком является то, что каждый раз, когда вы выполняете npm run unit, браузер Chrome будет появляться, а PhantomJS не будет.Рекомендуется использовать Chrome при отладке тестовых скриптов, и вы можете переключиться обратно на PhantomJS при запуске скриптов. и не нужно отлаживать.
4. Добавьте --auto-watch
По умолчанию автоотслеживание отключено, каждый раз, когда вы модифицируете тестовый скрипт или модифицируете код проекта, вам нужно вручную выполнить команду для запуска теста, что очень хлопотно. Мы можем добавить --auto-watch, чтобы во время разработки, если фича не прошла тестовый пример, разработчик мог сразу найти и исправить ее.
"unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --auto-watch",
Боевой проект
Пример 1
Сценарий: на странице есть поле ввода текстовой области и кнопка отправки, нажмите кнопку, чтобы отправить запрос. После нажатия кнопки «Отправить» внешний интерфейс должен проверить, соответствует ли контент формату json.Если он не соответствует, он сообщит, что его нельзя отправить.
Цель теста: проверить программу
Тестовый пример: с помощью покрытия условий введите числа, строки, неправильные строки json, 'null' и правильные строки json, чтобы проверить, выполняются ли все условия нормально, ожидая, что только последний случай — это возвращаемый результат. Да, все остальные терпят неудачу. .
// form-setting.vue测试校验功能
describe('form-setting.vue测试校验功能', () => {
const wrapper = shallowMount(formSetting, {
localVue
});
let vm = wrapper.vm;
it('test form填入数字是否会不通过', () => {
vm.appType = 'ios'; // 选择系统ios
vm.ios.schemeInfo = 1; // 输入数字
expect(vm.isValid()).to.equal(false);
});
it('test form填入字符串格式是否会不通过', () => {
vm.appType = 'ios';
vm.ios.schemeInfo = '1'; // 输入字符串
expect(vm.isValid()).to.equal(false);
});
it('test form填入错误json格式是否会不通过', () => {
vm.appType = 'ios';
vm.ios.schemeInfo = '{a:{a:}}'; // 输入非法的类似json格式的字符串
expect(vm.isValid()).to.equal(false);
});
it('test form填入空对象是否会不通过', () => {
vm.appType = 'ios';
vm.ios.schemeInfo = 'null'; // 输入null对象字符串
expect(vm.isValid()).to.equal(false);
});
it('test form填入正确JSON格式是否会通过', () => {
vm.appType = 'ios';
vm.ios.schemeInfo = '{"a": 111}'; // 输入正确的json字符串
expect(vm.isValid()).to.equal(true);
});
});
Пример 2
Сценарий: команда разработала подключаемый модуль проверки, функция которого состоит в том, чтобы проверить, удовлетворяет ли поле ввода соответствующим правилам.Если оно не соответствует соответствующим правилам, под полем ввода появится узел dom, указывающий на ошибку.
Тестовый пример: путем перечисления всех операций ввода, а затем определения наличия узла подсказки об ошибке с именем класса .error.
После завершения операции ввода, если контент не проходит проверку, страница сгенерирует узел dom с сообщением об ошибке. Этот процесс асинхронный, поэтому используйтеnextTick. Конкретное использование
return Vue.nextTick().then(() => {
...断言
}
Для подробного объяснения этого Vue Test Utils имеетСвязанная длина
import { mount, createLocalVue } from '@vue/test-utils'
import ValidateDemo from '@/components/validate-demo'
import validate from '@/directive/validate/1.0/validate'
import Vue from 'Vue'
const localVue = createLocalVue() // 创建一个Vue实例
localVue.use(validate) // 挂载校验插件
describe('测试validate-demo.vue', () => {
it('没发生输入操作,[不显示error]', () => {
const wrapper = mount(ValidateDemo, {
localVue
})
return Vue.nextTick().then(() => {
expect(wrapper.find('.error').exists()).to.equal(false)
})
})
it('聚焦输入框然后失去焦点,[显示error]', () => {
const wrapper = mount(ValidateDemo, {
localVue
})
let input = wrapper.find('input')
input.trigger('focus') // 聚焦
input.trigger('blur') // 失去焦点
return Vue.nextTick().then(() => {
expect(wrapper.find('.error').exists()).to.equal(true)
})
})
it('发生输入操作,然后清空,[显示error]', () => {
const wrapper = mount(ValidateDemo, {
localVue
})
let vm = wrapper.vm
let input = wrapper.find('input')
input.trigger('focus')
vm.name = '不为空'
vm.name = '' // 清空
input.trigger('blur')
return Vue.nextTick().then(() => {
expect(wrapper.find('.error').exists()).to.equal(true)
})
})
it('输入内容后,[不显示error]', () => {
const wrapper = mount(ValidateDemo, {
localVue
})
let vm = wrapper.vm
vm.name = '不为空' // 输入内容
return Vue.nextTick().then(() => {
expect(wrapper.find('.error').exists()).to.equal(false)
})
})
})
Ограничения модульного тестирования
Юнит-тестирование имеет много преимуществ, но это не обязательно означает, что оно подходит для каждого проекта, на мой взгляд, оно имеет следующие ограничения:
1. Дополнительное время
Даже если вы готовы потратить часть времени разработки на написание модульных тестов, как только функциональность изменится, это означает, что логика тестирования также должна быть скорректирована. Для некоторых часто меняющихся функций это может привести к большому объему обслуживания модульных тестов. Поэтому нам нужно взвесить все за и против, и мы можем рассмотреть возможность написания модульных тестов только для стабильных функций (например, некоторых общих компонентов) и основных процессов.
2. Не весь код можно протестировать
Если ваш проект полон кода с низкой степенью детализации, который связывает методы друг с другом, вы обнаружите, что модульное тестирование невозможно. Потому что модульное тестирование направлено на определение качества приложения по степени детализации кода. Столкнувшись с такой ситуацией, либо рефакторинг существующего кода, либо отказ от модульного тестирования и поиск других методов тестирования, таких как ручное тестирование и e2e-тестирование.
Хотя это недостаток модульного тестирования, я думаю,преимущество, Привычка писать юнит-тесты может подтолкнуть инженеров к улучшению детализации кода и более тщательному обдумыванию.
3. Работа всего процесса не может быть гарантирована
Внешний интерфейс — очень сложная среда тестирования, потому что каждый браузер уникален, а требуемые данные зависят от внутреннего интерфейса. Модульное тестирование может протестировать только каждую единицу функции.Для некоторых данных, которые зависят от API, их можно только имитировать, и они не могут по-настоящему имитировать реальные сценарии использования пользователями. В этом случае рекомендуется использовать другие методы тестирования, такие как ручное тестирование, e2e-тестирование.
Суммировать
Я думаю, что благодаря этому исследованию модульного тестирования самое большое сопротивление модульному тестированию:время.
Самым большим преимуществом ручного тестирования является то, что когда код функции написан, необходимо только вручную обновить браузер, чтобы фактически запустить его, чтобы определить, является ли программа правильной. Написание модульных тестов для этого потребует дополнительного времени разработки.
Но люди не машины, и как бы ни были простые вещи могут пойти не так. После добавления новых функций в систему мы обычно не тестируем старые функции вручную. Потому что это долго и скучно, и мы всегда думаем, что код, который мы пишем, не повлияет на старый функционал.
Однако можно подумать об этом и с другой стороны: если соответствующие юнит-тесты пишутся при разработке старых функций, то каждый раз перед входом в фазу тестирования старые функции можно прогонять через тестовый скрипт. Это экономит время на тестирование старых функций и обеспечивает душевное спокойствие: я могу быть уверен, что код, который я пишу, проходит тесты, несмотря ни на что.
Наконец, спасибо за чтение.Эта статья представляет собой исследование относительно простого модульного теста для проекта Vue.Это руководство.Если есть какие-либо необоснованные места или предложения, вы можете исправить меня!