>  기사  >  웹 프론트엔드  >  Vue 단위 테스트에서 Karma+Mocha에 대한 자세한 설명

Vue 단위 테스트에서 Karma+Mocha에 대한 자세한 설명

亚连
亚连원래의
2018-06-08 16:56:331855검색

이 글은 주로 Vue 유닛 테스트 Karma+Mocha 학습 노트에 대한 자세한 설명을 소개하고 있습니다.

vue-cli를 사용하여 프로젝트를 생성하면 단위 테스트와 e2e 테스트를 설치할지 묻는 메시지가 표시됩니다. 우리는 공식적으로 이 두 가지 테스트 프레임워크를 사용하도록 권장하므로, 배우고 실습해 보겠습니다.

소개

Karma

Karma는 Node.js 기반의 JavaScript 테스트 실행 프로세스 관리 도구(Test Runner)입니다. Vue에서 이 도구의 주요 기능은 테스트를 위해 다양한 주류 웹 브라우저에서 프로젝트를 실행하는 것입니다.

즉, 브라우저 환경에서 코드를 테스트할 수 있는 테스트 도구입니다. 필요한 이유는 코드가 브라우저 측에서 실행되도록 설계되었을 수 있으며, 노드 환경에서 테스트할 때 일부 버그가 노출되지 않을 수도 있기 때문입니다. 또한, 브라우저에 호환성 문제가 있는 경우 Karma는 이에 대한 수단을 제공합니다. 여러 브라우저에서 코드를 자동으로 실행합니다. 브라우저(크롬, 파이어폭스 등) 환경에서 실행합니다. 코드가 노드 측에서만 실행되는 경우 카르마를 사용할 필요가 없습니다.

Mocha

Mocha는 chai 어설션 라이브러리를 사용하여 vue-cli에서 단위 테스트를 구현하는 테스트 프레임워크입니다.

Chai 어설션 라이브러리에 대해서는 Chai.js 어설션 라이브러리 API 중국어 문서를 확인하고 사용하면 매우 간단합니다.

테스트 프레임워크에 대한 나의 이해

npm rununit 실행 프로세스

  1. npm rununit 명령 실행

  2. Karma 실행 환경 열기

  3. Mocha를 사용하여 작성한 것을 하나씩 테스트해 보세요. Chai 어설션 사용 테스트 사례

  4. 는 테스트 결과를 터미널

  5. 에 표시합니다. 테스트가 성공하면 karma-coverage는 ./test/unit/coverage 폴더에 테스트 적용 범위 결과가 포함된 웹 페이지를 생성합니다.

Karma

Karma의 경우 구성 옵션에 대해 방금 배웠습니다.

다음은 간단한 설명이 포함된 Vue의 카르마 구성입니다.

var webpackConfig = require('../../build/webpack.test.conf')

module.exports = function (config) {
 config.set({
  // 浏览器
  browsers: ['PhantomJS'],
  // 测试框架
  frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'],
  // 测试报告
  reporters: ['spec', 'coverage'],
  // 测试入口文件
  files: ['./index.js'],
  // 预处理器 karma-webpack
  preprocessors: {
   './index.js': ['webpack', 'sourcemap']
  },
  // Webpack配置
  webpack: webpackConfig,
  // Webpack中间件
  webpackMiddleware: {
   noInfo: true
  },
  // 测试覆盖率报告
  // https://github.com/karma-runner/karma-coverage/blob/master/docs/configuration.md
  coverageReporter: {
   dir: './coverage',
   reporters: [
    { type: 'lcov', subdir: '.' },
    { type: 'text-summary' }
   ]
  }
 })
}

Mocha and chai

공식 예제를 살펴보겠습니다(모두 코드의 의미를 설명하는 설명 포함):

import Vue from 'vue' // 导入Vue用于生成Vue实例
import Hello from '@/components/Hello' // 导入组件
// 测试脚本里面应该包括一个或多个describe块,称为测试套件(test suite)
describe('Hello.vue', () => {
 // 每个describe块应该包括一个或多个it块,称为测试用例(test case)
 it('should render correct contents', () => {
  const Constructor = Vue.extend(Hello) // 获得Hello组件实例
  const vm = new Constructor().$mount() // 将组件挂在到DOM上
  //断言:DOM中class为hello的元素中的h1元素的文本内容为Welcome to Your Vue.js App
  expect(vm.$el.querySelector('.hello h1').textContent)
   .to.equal('Welcome to Your Vue.js App') 
 })
})

Knowledge 알아야 할 사항:

  1. 테스트 스크립트는 test/unit/specs/ 디렉터리에 있어야 합니다.

  2. 스크립트 명명 방법은 [컴포넌트 이름].spec.js 입니다.

  3. 소위 주장은 구성 요소에 대해 일부 작업을 수행하고 결과를 예측하는 것입니다. 테스트 결과가 어설션과 동일하면 테스트가 통과됩니다.

  4. 기본적으로 유닛 테스트는 main.js를 제외한 src 디렉터리의 모든 파일을 테스트하며, test/unit/index.js 파일에서 수정할 수 있습니다.

  5. Chai 어설션 라이브러리에서 to be는 that have and has have with at of at과 동일하며 의미가 없으며 단지 이해하기 쉽도록 하기 위한 것입니다.

  6. 테스트 스크립트는 여러 개의 descibe로 구성되며, 각 설명은 여러 개의 it으로 구성됩니다.

  7. 비동기 테스트 이해하기

it('异步请求应该返回一个对象', done => {
  request
  .get('https://api.github.com')
  .end(function(err, res){
   expect(res).to.be.an('object');
   done();
  });
});

Describe

describe('hooks', function() {

 before(function() {
  // 在本区块的所有测试用例之前执行
 });

 after(function() {
  // 在本区块的所有测试用例之后执行
 });

 beforeEach(function() {
  // 在本区块的每个测试用例之前执行
 });

 afterEach(function() {
  // 在本区块的每个测试用例之后执行
 });

 // test cases
});

Practice

의 후크(라이프 사이클) 이해하기위에서는 단위 테스트 사용법을 간략하게 소개했으니 Vue에서 단위 테스트를 시작하겠습니다!

util.js

공식 Vue 데모에서 볼 수 있듯이 Vue 단위 테스트를 위해 구성 요소를 Vue 인스턴스로 인스턴스화해야 하며 때로는 DOM에 마운트해야 합니다.

 const Constructor = Vue.extend(Hello) // 获得Hello组件实例
 const vm = new Constructor().$mount() // 将组件挂载到DOM上

위의 작성 방법은 단지 구성 요소를 가져오기 위한 것입니다. 때로는 props 속성, 사용자 정의 메서드 등을 전달해야 할 수도 있고 타사 UI 프레임워크를 사용해야 할 수도 있습니다. 그래서 위의 작성 방법은 매우 번거롭습니다.

여기에서는 Vue 단위 테스트에서 일반적으로 사용되는 방법을 캡슐화하는 Element의 단위 테스트 도구 스크립트 Util.js를 권장합니다. 다음 데모도 Util.js를 기반으로 작성되었습니다.
다음은 각 방법의 목적에 대한 간략한 설명입니다.

/**
 * 回收 vm,一般在每个测试脚本测试完成后执行回收vm。
 * @param {Object} vm
 */
exports.destroyVM = function (vm) {}

/**
 * 创建一个 Vue 的实例对象
 * @param {Object|String} Compo   - 组件配置,可直接传 template
 * @param {Boolean=false} mounted  - 是否添加到 DOM 上
 * @return {Object} vm
 */
exports.createVue = function (Compo, mounted = false) {}

/**
 * 创建一个测试组件实例
 * @param {Object} Compo     - 组件对象
 * @param {Object} propsData   - props 数据
 * @param {Boolean=false} mounted - 是否添加到 DOM 上
 * @return {Object} vm
 */
exports.createTest = function (Compo, propsData = {}, mounted = false) {}

/**
 * 触发一个事件
 * 注: 一般在触发事件后使用 vm.$nextTick 方法确定事件触发完成。
 * mouseenter, mouseleave, mouseover, keyup, change, click 等
 * @param {Element} elm   - 元素
 * @param {String} name   - 事件名称
 * @param {*} opts      - 配置项
 */
exports.triggerEvent = function (elm, name, ...opts) {}

/**
 * 触发 “mouseup” 和 “mousedown” 事件,既触发点击事件。
 * @param {Element} elm   - 元素
 * @param {*} opts     - 配置选项
 */
exports.triggerClick = function (elm, ...opts) {}

예제 1

예제 1에서는 Hello 컴포넌트의 다양한 요소의 데이터를 테스트하고 util.js의 destroyVM 및 createTest 메소드 사용법과 테스트할 대상 요소를 얻는 방법을 배웠습니다. DOM 요소를 얻는 방법은 DOM 개체 튜토리얼을 참조하세요.

Hello.vue

<template>
 <p class="hello">
  <h1 class="hello-title">{{ msg }}</h1>
  <h2 class="hello-content">{{ content }}</h2>
 </p>
</template>

<script>
export default {
 name: &#39;hello&#39;,
 props: {
  content: String
 },
 data () {
  return {
   msg: &#39;Welcome!&#39;
  }
 }
}
</script>

Hello.spec.js

import { destroyVM, createTest } from &#39;../util&#39;
import Hello from &#39;@/components/Hello&#39;

describe(&#39;Hello.vue&#39;, () => {
 let vm

 afterEach(() => {
  destroyVM(vm)
 })

 it(&#39;测试获取元素内容&#39;, () => {
  vm = createTest(Hello, { content: &#39;Hello World&#39; }, true)
  expect(vm.$el.querySelector(&#39;.hello h1&#39;).textContent).to.equal(&#39;Welcome!&#39;)
  expect(vm.$el.querySelector(&#39;.hello h2&#39;).textContent).to.have.be.equal(&#39;Hello World&#39;)
 })

 it(&#39;测试获取Vue对象中数据&#39;, () => {
  vm = createTest(Hello, { content: &#39;Hello World&#39; }, true)
  expect(vm.msg).to.equal(&#39;Welcome!&#39;)
  // Chai的语言链是无意义的,可以随便写。如下:
  expect(vm.content).which.have.to.be.that.equal(&#39;Hello World&#39;) 
 })

 it(&#39;测试获取DOM中是否存在某个class&#39;, () => {
  vm = createTest(Hello, { content: &#39;Hello World&#39; }, true)
  expect(vm.$el.classList.contains(&#39;hello&#39;)).to.be.true
  const title = vm.$el.querySelector(&#39;.hello h1&#39;)
  expect(title.classList.contains(&#39;hello-title&#39;)).to.be.true
  const content = vm.$el.querySelector(&#39;.hello-content&#39;)
  expect(content.classList.contains(&#39;hello-content&#39;)).to.be.true
 })
})

출력 결과

Hello.vue
√ 요소 콘텐츠를 가져오기 위한 테스트
√ Vue 객체에서 데이터를 가져오기 위한 테스트
√ 특정 요소 내용을 가져오기 위한 테스트 클래스가 DOM에 존재합니다

예제 2

예제 2에서는 createTest를 사용하여 클릭 이벤트를 테스트하기 위한 테스트 구성 요소를 생성하고, createVue를 사용하여 Click 구성 요소의 사용을 테스트하기 위한 Vue 샘플 객체를 생성했습니다. 여기에서는 주로 createVue 메소드의 사용을 볼 수 있습니다.

Click.vue

<template>
 <p>
  <span class="init-num">初始值为{{ InitNum }}</span><br>
  <span class="click-num">点击了{{ ClickNum }}次</span><br>
  <span class="result-num">最终结果为{{ ResultNum }}</span><br>
  <button @click="add">累加{{ AddNum }}</button>
 </p>
</template>

<script>
export default {
 name: &#39;Click&#39;,
 props: {
  AddNum: {
   type: Number,
   default: 1
  },
  InitNum: {
   type: Number,
   default: 1
  }
 },
 data () {
  return {
   ClickNum: 0,
   ResultNum: 0
  }
 },
 mounted () {
  this.ResultNum = this.InitNum
 },
 methods: {
  add () {
   this.ResultNum += this.AddNum
   this.ClickNum++
   this.$emit(&#39;result&#39;, {
    ClickNum: this.ClickNum,
    ResultNum: this.ResultNum
   })
  }
 }
}
</script>

Click.spec.js

import { destroyVM, createTest, createVue } from &#39;../util&#39;
import Click from &#39;@/components/Click&#39;

describe(&#39;click.vue&#39;, () => {
 let vm

 afterEach(() => {
  destroyVM(vm)
 })

 it(&#39;测试按钮点击事件&#39;, () => {
  vm = createTest(Click, {
   AddNum: 10,
   InitNum: 11
  }, true)
  let buttonElm = vm.$el.querySelector(&#39;button&#39;)
  buttonElm.click()
  buttonElm.click()
  buttonElm.click()
  // setTimeout 的原因
  // 在数据改变之后,界面的变化会有一定延时。不用timeout有时候会发现界面没有变化
  setTimeout(done => {
   expect(vm.ResultNum).to.equal(41)
   expect(vm.$el.querySelector(&#39;.init-num&#39;).textContent).to.equal(&#39;初始值为11&#39;)
   expect(vm.$el.querySelector(&#39;.click-num&#39;).textContent).to.equal(&#39;点击了3次&#39;)
   expect(vm.$el.querySelector(&#39;.result-num&#39;).textContent).to.equal(&#39;最终结果为41&#39;)
   done()
  }, 100)
 })

 it(&#39;测试创建Vue对象&#39;, () => {
  let result
  vm = createVue({
   template: `
    <click @click="handleClick"></click>
   `,
   props: {
    AddNum: 10,
    InitNum: 11
   },
   methods: {
    handleClick (obj) {
     result = obj
    }
   },
   components: {
    Click
   }
  }, true)
  vm.$el.click()
  vm.$nextTick(done => {
   expect(result).to.be.exist
   expect(result.ClickNum).to.equal(1)
   expect(result.ResultNum).to.be.equal(21)
   done()
  })
})

출력 결과

click.vue
√ 버튼 클릭 이벤트 테스트
√ Vue 객체 생성 테스트

Others

모든 샘플 코드는 Github에 있습니다. 저장소 보기가 쉽습니다. 더 좋은 테스트 사례를 보고 싶다면 Util.js를 사용하여 Element의 단위 테스트 스크립트 작성 방법을 살펴보는 것이 좋습니다. 우리가 배워야 할 테스트 스크립트가 많이 있습니다. 대부분의 Vue 사용자가 사용하는 UI 컴포넌트 라이브러리이기 때문에 테스트 스크립트는 매우 잘 작성되어야 합니다~ 이 스크립트를 복사해 두셔도 Vue 컴포넌트의 단위 테스트를 학습하는데 큰 도움이 될 것이라고 믿습니다.

다음은 참고용으로 Element 단위 테스트를 읽으며 제가 메모한 내용입니다.

Util.js 方法包含了大多数Vue组件化的测试需求。

vm.$el vm.$nextTick 和 vm.$ref 都是在测试过程中比较常用的一些Vue语法糖。

需要注意: vm.$nextTick 方法是异步的,所以需要在里面使用done方法。

异步断言,方法参数需要是 _ 或者 done

大多数时候查询元素通过 querySelector 方法查询class获得

vm.$el.querySelector(&#39;.el-breadcrumb&#39;).innerText

大多数情况下查询是否存在某个Class通过 classList.contains 方法获得,查找的结果为 true 或 false

vm.$el .classList.contains(&#39;el-button--primary&#39;)

异步测试必须以 done() 方法结尾。setTimeout 和 vm.$nextTick 是常用的异步测试。

实现按钮点击:通过获取按钮元素 btn,执行 btn.click() 方法实现。

由于 Vue 进行异步更新DOM 的情况,一些依赖DOM更新结果的断言必须在 Vue.nextTick 回调中进行。

triggerEvent(vm.$refs.cascader.$el, &#39;mouseenter&#39;);
vm.$nextTick(_ => {
   vm.$refs.cascader.$el.querySelector(&#39;.el-cascader__clearIcon&#39;).click();
   vm.$nextTick(_ => {
    expect(vm.selectedOptions.length).to.be.equal(0);
    done();
   });
});

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在mint-ui中使用时间插件及获取选择值

VUE2实现二级省市联动选择

使用react实现分页组件

위 내용은 Vue 단위 테스트에서 Karma+Mocha에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.