BDD(Behavior Driven Development)行为驱动开发
集成测试: 完全是用测试脚本去模拟用户操作,比如打开浏览器,点击登录,输入用户名,密码等。然后判断是否达到预期的结果来进行测试。
概念解释:
story:一个场景,可以理解成一个步骤,用户某一阶段操作的集合。
比如在例子TodoList中,用户在header输入框中输入内容,敲击enter键,对应下面任务列表新增一个任务。这就是一个story。
先看一个BDD的例子:
import { mount } from '@vue/test-utils'
import TodoList from '../../TodoList.vue'
import { createWrapper } from '../../../../utils/utilTest'
it(`
1. 输入内容
2. 触发事件
3. 列表项增加
`,async () => {
const wrapper = mount(TodoList)
let inputEle = createWrapper(wrapper, 'test-input').at(0)
const content = "hello"
inputEle.setValue(content)
await inputEle.trigger('change')
await inputEle.trigger("keyup.enter")
const listItem = createWrapper(wrapper,'test-item')
expect(listItem.length).toBe(1)
expect(listItem.at(0).text()).toContain(content)
})
首先模拟用户行为,分为三个步骤,第一步输入内容,第二步触发事件,第三步对应列表项增加,依次步骤为指导然后写测试用例,在写的过程中,由于模拟用户行为时,进行了跨组件测试,因此使用集成测试的方案,使用mount
进行深度渲染。注意在写有触发事件的测试用例中,使用async/await
异步处理方式,否则时间执行结果无法获取到。
通过例子分析TDD与BDD两种模式
TDD:
- 先写测试再写代码
- 一般结合单元测试使用,是白盒测试(看着代码测试,测试代码与业务代码耦合度高)
- 测试重点在代码
- 安全感低(单个组件或模块通过测试,不代表组合在一起就没问题)
- 速度快(采用shallowMonut方法,只渲染一层)
BDD:
- 先写代码再写测试
- 一般结合集成测试使用,是黑盒测试(从用户角度只关注界面,行为,结果。一般涉及到多组件,不知道具体代码逻辑)
- 测试重点在UI(DOM)
- 安全感高(如果测试通过,即可认为在用户端可以放心使用)
- 速度慢(采用mount方法,将整个组件树都挂载出来)
Vuex项目测试例子
import { mount } from '@vue/test-utils'
import TodoList from '../../TodoList.vue'
import { createWrapper } from '../../../../utils/utilTest'
import store from '../../../../store'
it(`
1. 输入内容
2. 触发事件
3. 列表项增加
`, async () => {
const wrapper = mount(TodoList, {
store
})
var inputEle = createWrapper(wrapper, 'test-input').at(0)
const content = 'hello'
inputEle.setValue(content)
await inputEle.trigger('change')
await inputEle.trigger('keyup.enter')
const listItem = createWrapper(wrapper, 'test-item')
expect(listItem.length).toBe(1)
expect(listItem.at(0).text()).toContain(content)
})
在项目中使用了Vuex时,在测试时需要将store引入到测试组件中,否则测试组件中是没有Vuex的。具体就是在渲染组件的时候进行传参。
const wrapper = mount(TodoList, {
store
})
两种测试模式混合使用
比如上面代码是对项目功能进行的集成测试
同时也可以在项目中再使用单元测试
比如对Vuex中的逻辑进行单元测试
import store from '../../../../store'
it("测试提交commit inputValue跟着变化", () => {
const content = '123'
store.commit("changeInputValue",content)
expect(store.state.inputValue).toBe(content)
})
两种测试模式如何选取?
测试业务的时候一般选择BDD+集成测试
测试函数库的时候优先选择TDD+单元测试
一般情况下可以采用两种模式结合使用对项目进行测试。
异步测试
比如在组件中需要使用axios获取数据并显示到页面中,此时的测试策略如下:
首先前端测试不去测试请求返回值的格式以及数据正确与否,这一步应该是由后端支持。否则通过调取后端数据既耗时又耗资源,对于前端测试来说比较慢,等等。
然后异步测试主要是前端根据与后端协商好的数据格式等,自行模拟一个前端数据,然后进行测试。
在containers/TodoList文件夹下创建__mocks__
文件夹然后创建一个axios.js文件
const list = {
success: true,
data: [
{
type: 'div',
value: 'hello'
},
{
type: 'div',
value: 'sasasa'
}
]
}
export default {
get(url) {
if (url == "/getList.json") {
return new Promise((resolved, rejected) => {
setTimeout(() => {
resolved(list)
}, 3000)
})
}
}
}
在__tests__/integration/TodoList.js
import { mount } from '@vue/test-utils'
import TodoList from '../../TodoList.vue'
import { createWrapper } from '../../../../utils/utilTest'
import store from '../../../../store'
beforeEach(()=>{
jest.useFakeTimers()
})
it(`
1. 首次进入从远程获取数据
2. 将数据展示到列表中
`, () => {
const wrapper = mount(TodoList, {
store
})
jest.runAllTimers()
wrapper.vm.$nextTick().then(() => {
const listItems = createWrapper(wrapper, 'test-item')
expect(listItems.length).toBe(2)
})
})
需要注意的是:使用setTimeout的时候需要使用jest.useFakeTimers()
去做处理,然后在使用异步获取数据的时候,需要使用Vue实例的nextTick()
方法中再去做断言。
模拟异步失败测试:比如axios获取数据失败,组件应该显示0条数据
设置一个变量控制axios的resolved和rejected执行
export default {
get(url) {
if (url == "/getList.json") {
return new Promise((resolved, rejected) => {
if(this.success){
resolved(list)
}else{
rejected(new Error())
}
})
}
}
}
在测试用例中:
import axios from '../../__mocks__/axios.js'
beforeEach(()=>{
axios.success = true
})
it(`
1. 首次进入从远程获取数据失败时应该展示0条
2. 将数据展示到列表中
`, (done) => {
axios.success = false
const wrapper = mount(TodoList, {
store
})
wrapper.vm.$nextTick().then(() => {
const listItems = createWrapper(wrapper, 'test-item')
expect(listItems.length).toBe(0)
done()
})
})
注意,由于$nextTick为异步执行,所以需要配合done方法使用,否则测试代码只会走到nextTick上面便认为测试通过。
网友评论