前端自动化测试

为什么要进行测试

  1. 测试可以确保得到预期的结果
  2. 作为现有代码⾏为的描述
  3. 促使开发者写可测试的代码,⼀般可测试的代码可读性也会⾼⼀点
  4. 如果依赖的组件有修改,受影响的组件能在测试中发现错误

测试分类

单元测试:指的是以原件的单元为单位,对软件进⾏测试。单元可以是⼀个函数,也可以是⼀个模块或⼀个组件,基本特征就是只要输⼊不变,必定返回同样的输出。⼀个软件越容易些单元测试,就表明它的模块化结构越好,给模块之间的耦合越弱。React的组件化和函数式编程,天⽣适合进⾏单元测试

功能测试:相当于是⿊盒测试,测试者不了解程序的内部情况,不需要具备编程语⾔的专⻔知识,只知道程序的输⼊、输出和功能,从⽤户的⻆度针对软件界⾯、功能和外部结构进⾏测试,不考虑内部的逻辑

集成测试:在单元测试的基础上,将所有模块按照设计要求组装成⼦系统或者系统,进⾏测试

冒烟测试:在正式全⾯的测试之前,对主要功能进⾏的与测试,确认主要功能是否满⾜需要,软件是否能正常运⾏

组件的单元测试有很多好处:

  • 提供描述组件⾏为的⽂档
  • 节省⼿动测试的时间
  • 减少研发新特性时产⽣的 bug
  • 改进设计
  • 促进重构
1.jpg

测试⼯具

2.jpg

单测

单元测试(unit testing),是指对软件中的最⼩可测试单元进⾏检查和验证。

在vue中,推荐⽤Mocha+chai 或者jest,咱们使⽤jest演示,语法基本⼀致

新建kaikeba.spec.js,.spec.js是命名规范,写下⼀下代码

function add(num1, num2) {
 return num1 + num2
}
describe('Kaikeba', () => {
 it('测试加法', () => {
 expect(add(1, 3)).toBe(3)
 expect(add(1, 3)).toBe(4)
 expect(add(-2, 3)).toBe(1)
 })
})

执⾏ npm run test:unit


3.jpg

api介绍

  • describe : 定义⼀个测试套件
  • it :定义⼀个测试⽤例
  • expect :断⾔的判断条件
  • toBe :断⾔的⽐较结果

测试Vue组件

<template>
 <div>
 <span>{{ message }}</span>
 <button @click="changeMsg">点击</button>
 </div>
</template> <script>
 export default {
 data () {
 return {
 message: 'vue-text'
 }
 },
 created () {
 this.message = '开课吧'
 },
 methods:{
 changeMsg(){
 this.message = '按钮点击'
 }
 }
 }
</script>
// 导⼊ Vue.js 和组件,进⾏测试
import Vue from 'vue'
import KaikebaComp from '@/components/Kaikeba.vue'
// 这⾥是⼀些 Jasmine 2.0 的测试,你也可以使⽤你喜欢的任何断⾔库或测试⼯具。
describe('KaikebaComp', () => {
 // 检查原始组件选项
 it('由created⽣命周期', () => {
 expect(typeof KaikebaComp.created).toBe('function')
 })
 // 评估原始组件选项中的函数的结果
 it('初始data是vue-text', () => {
 expect(typeof KaikebaComp.data).toBe('function')
 const defaultData = KaikebaComp.data()
 expect(defaultData.message).toBe('hello!')
 })
})
4.jpg

检查mounted之后

it('mount之后测data是开课吧', () => {
 const vm = new Vue(KaikebaComp).$mount()
 expect(vm.message).toBe('开课吧')
 })

用户点击

和写vue 没啥本质区别,只不过我们⽤测试的⻆度去写代码,vue提供了专⻔针对测试的 @vue/test-utils,可以区官网查看更多API

it('按钮点击后', () => {
 const wrapper = mount(KaikebaComp)
 wrapper.find('button').trigger('click')
 expect(wrapper.vm.message).toBe('按钮点击')
 // 测试html渲染结果
 expect(wrapper.find('span').html()).toBe('<span>按钮点击</span>')
 })

测试覆盖率

jest⾃带覆盖率,如果⽤的mocha,需要使⽤istanbul来统计覆盖率
package.json⾥修改jest配置

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

在此执⾏npm run test:unit

5.jpg

可以看到我们kaikeba.vue的覆盖率是100%,我们修改⼀下代码

<template>
 <div>
 <span>{{ message }}</span>
 <button @click="changeMsg">点击</button>
 </div>
</template> <script>
export default {
 data() {
 return {
 message: "vue-text",
 count: 0
 };
},
 created() {
 this.message = "开课吧";
 },
 methods: {
 changeMsg() {
 if (this.count > 1) {
 this.message = "count⼤于1";
 } else {
 this.message = "按钮点击";
 }
 },
 changeCount() {
 this.count += 1;
 }
 }
};
</script>
6.jpg

现在的代码,依然是测试没有报错,但是覆盖率只有66%了,⽽且没有覆盖的代码⾏数,都标记了出来

Jest详解

beforeAll(() => {
 console.log('global before all');
});
afterAll(() => {
 console.log('global after all');
});
beforeEach(() =>{
 console.log('global before each');
});
afterEach(() => {
 console.log('global after each');
});
describe('test1', () => {
 beforeAll(() => {
 console.log('test1 before all');
 });
 
 afterAll(() => {
 console.log('test1 after all');
 });
 
 beforeEach(() => {
 console.log('test1 before each');
 });
 
 afterEach(() => {
 console.log('test1 after each');
 });
 
 it('test sum', () => {
 expect(sum(2, 3)).toEqual(5);
 });
 
 it('test mutil', () => {
 expect(sum(2, 3)).toEqual(7);
 });
 
});

断言

  1. expect(value):要测试⼀个值进⾏断⾔的时候,要使⽤expect对值进⾏包裹
  2. toBe(value):使⽤Object.is来进⾏⽐较,如果进⾏浮点数的⽐较,要使⽤toBeCloseTo
  3. not:⽤来取反
  4. oEqual(value):⽤于对象的深⽐较
  5. toMatch(regexpOrString):⽤来检查字符串是否匹配,可以是正则表达式或者字符串
  6. toContain(item):⽤来判断item是否在⼀个数组中,也可以⽤于字符串的判断
  7. toBeNull(value):只匹配null
  8. toBeUndefined(value):只匹配undefined
  9. toBeDefined(value):与toBeUndefined相反
  10. toBeTruthy(value):匹配任何使if语句为真的值
  11. toBeFalsy(value):匹配任何使if语句为假的值
  12. toBeGreaterThan(number): ⼤于
  13. toBeGreaterThanOrEqual(number):⼤于等于
  14. toBeLessThan(number):⼩于
  15. toBeLessThanOrEqual(number):⼩于等于
  16. toBeInstanceOf(class):判断是不是class的实例
  17. anything(value):匹配除了null和undefined以外的所有值
  18. resolves:⽤来取出promise为fulfilled时包裹的值,⽀持链式调⽤
  19. rejects:⽤来取出promise为rejected时包裹的值,⽀持链式调⽤
  20. toHaveBeenCalled():⽤来判断mock function是否被调⽤过
  21. toHaveBeenCalledTimes(number):⽤来判断mock function被调⽤的次数
  22. assertions(number):验证在⼀个测试⽤例中有number个断⾔被调⽤
  23. extend(matchers):⾃定义⼀些断⾔

方法

  1. simulate(event, mock):模拟事件,⽤来触发事件,event为事件名称,mock为⼀个event object
  2. instance():返回组件的实例
  3. find(selector):根据选择器查找节点,selector可以是CSS中的选择器,或者是组件的构造函数,
    组件的display name等
  4. at(index):返回⼀个渲染过的对象
  5. get(index):返回⼀个react node,要测试它,需要重新渲染
  6. contains(nodeOrNodes):当前对象是否包含参数重点 node,参数类型为react对象或对象数组
  7. text():返回当前组件的⽂本内容
  8. html(): 返回当前组件的HTML代码形式
  9. props():返回根组件的所有属性
  10. prop(key):返回根组件的指定属性

E2E测试

借⽤浏览器的能⼒,站在⽤户测试⼈员的⻆度,输⼊框,点击按钮等,完全模拟⽤户,这个和具体的框架关系不⼤,完全模拟浏览器⾏为.

也被称为端到端测试,启动之后会启动一个类似于浏览器的窗口自动执行,因为需求变更等原因,E2E代码测试编码多,甚至可能为了测试一行代码需要写十行测试代码,针对多变的场景不适合,最多针对登录管理等功能固定的页面可以写一些E2E测试。

对比单元测试:单元测试针对vue甚至可以针对到单一组件,更具象,所以一般只是写单元测试。

修改e2e/spec/test.js

// https://docs.cypress.io/api/introduction/api.html
describe('端到端测试,抢测试⼈员的饭碗', () => {
 it('先访问⼀下', () => {
 cy.visit('/')
 // cy.contains('h1', 'Welcome to Your Vue.js App')
 cy.contains('#message', '开课吧')
 })
})
7.jpg

可以看到是打开了⼀个浏览器进⾏测试

测试⽤户点击

// https://docs.cypress.io/api/introduction/api.html
describe('端到端测试,抢测试⼈员的饭碗', () => {
 it('先访问⼀下', () => {
 cy.visit('/')
 // cy.contains('h1', 'Welcome to Your Vue.js App')
 cy.contains('#message', '开课吧')
 cy.get('button').click()
 cy.contains('#message', '按钮点击')
 })
})

TDD

所以TDD 就是测试驱动开发模式,就是我们开发⼀个新功能,先把测试写好,然后测试跑起来,会报错,我们再开始写代码,挨个的把测试跑绿,功能也就完成了

React 自动化测试

React中,也是使⽤jest来做⾃动化测试

https://jestjs.io/docs/en/tutorial-react

总结

测试会极大拖慢开发进度,但是如果测试一旦也写好。例如单元测试,在package.json文件中配置了,则每次保存或者构建之前都会执行一下测试的函数,例如写了add函数,多成员开发,B修改了该函数,则测试不通过,连项目都跑不起来,则保证了项目的健壮性。

代码地址:https://gitee.com/zengqiang_455/qianduanzidonghuaceshixiaodemo

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352