基礎和配置請查看上篇文章
TDD開發 todoList
在src下創建文件containers 如下
TDD開發
app.vue
<template>
<div id="app">
<TodoList/>
</div>
</template>
<script>
import TodoList from './containers/TodoList'
export default {
name: 'app',
components: {
TodoList
}
}
</script>
<style lang="stylus">
*{
margin:0;
padding 0;
}
html,body{
height 100%;
background :#CDCDCD;
}
</style>
先寫測試用例
todoList.test.js
import { shallowMount } from '@vue/test-utils'
import TodoList from '../../TodoList.vue'
import Undolist from '../../components/Undolist.vue'
// shallowMount淺渲染,只渲染當前組件,不渲染包含的子組件,適合單元測試,
// mount 全渲染,適合集成測試
describe('TodoList.vue', () => {
it('TodoItem 初始化時,undoList應該爲空', () => {
const wrapper = shallowMount(TodoList)
const undoList = wrapper.vm.$data.undoList
expect(undoList).toEqual([])
})
it('Todolist 監聽到header的add事件是,,undoList數組會增加一個內容', () => {
const wrapper = shallowMount(TodoList)
wrapper.vm.addUntodoItem(1)
expect(wrapper.vm.undoList).toEqual([1])
})
it('Todolist 調用 UndoList 應該傳遞 list 參數', () => {
const wrapper = shallowMount(TodoList)
wrapper.setData({ undoList: [1, 2, 3] })
const undolist = wrapper.find(Undolist)
const list = undolist.props('list')
expect(list).toEqual([1, 2, 3])
})
it('Todolist 調用 delUndoItem方法 undoList列表內容會減少一個 ', () => {
const wrapper = shallowMount(TodoList)
wrapper.setData({ undoList: [1, 2, 3] })
wrapper.vm.delUndoItem(1)
expect(wrapper.vm.undoList).toEqual([1, 3])
})
todoList.vue
<template>
<div class="todoList">
<Header @add="addUntodoItem"></Header>
<UndoList @delUndoItem="delUndoItem" :list="undoList"></UndoList>
</div>
</template>
<script>
import Header from './components/Header'
import UndoList from './components/UndoList'
export default {
name: 'TodoList',
components: {
Header,
UndoList
},
data () {
return {
undoList: []
}
},
methods: {
addUntodoItem (data) {
this.undoList.push(data)
},
delUndoItem (data) {
this.undoList.splice(data, 1)
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="stylus">
.todoList{
width :100%;
height :100%
}
</style>
組件
header.test.js
import { shallowMount } from '@vue/test-utils'
import Header from '../../components/Header.vue'
// shallowMount淺渲染,只渲染當前組件,不渲染包含的子組件,適合單元測試,
// mount 全渲染,適合集成測試
describe('Header.vue', () => {
it('Header 樣式發送改變, 做提示', () => {
const wrapper = shallowMount(Header)
// 生成快照,當頁面樣式寫好後可以添加快照,用於提示
expect(wrapper).toMatchSnapshot()
})
it('header 包含 input 框', () => {
const wrapper = shallowMount(Header)
const input = wrapper.find('[data-test="input"]')
// 判斷input原生是否存在
expect(input.exists()).toBe(true)
})
it('input 框 初始內容爲空', () => {
const wrapper = shallowMount(Header)
const inputValue = wrapper.vm.$data.inputValue
expect(inputValue).toBe('')
})
it('Header 中 input 框 值發送變化,數據應該跟着變化', () => {
const wrapper = shallowMount(Header)
const input = wrapper.find('[data-test="input"]')
input.setValue('TodoList')
const inputValue = wrapper.vm.$data.inputValue
expect(inputValue).toBe('TodoList')
})
it('Header 中 input 框 無內容時,按回車不觸發事件', () => {
const wrapper = shallowMount(Header)
const input = wrapper.find('[data-test="input"]')
input.setValue('')
input.trigger('keyup.enter')
// 是否向父組件提交add事件
expect(wrapper.emitted().add).toBeFalsy()
})
it('Header 中 input 框 有內容時,按回車觸發事件 同時清空', () => {
const wrapper = shallowMount(Header)
const input = wrapper.find('[data-test="input"]')
input.setValue('Todolist')
input.trigger('keyup.enter')
const inputValue = wrapper.vm.inputValue
// 是否向父組件提交add事件
expect(wrapper.emitted().add).toBeTruthy()
expect(inputValue).toBe('')
})
})
header.vue
<template>
<div class="header">
<div class="title">TodoList</div>
<input
class="header-input"
type="text"
data-test="input"
@keyup.enter="add"
v-model="inputValue"
placeholder="todoItem"
>
</div>
</template>
<script>
export default {
name: 'Header',
data () {
return {
inputValue: ''
}
},
methods: {
add () {
if (this.inputValue) {
this.$emit('add', this.inputValue)
this.inputValue = ''
}
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="stylus">
.header{
height :60px;
background :#666;
display :flex;
justify-content :center;
align-items :center;
}
.title{
font-size :40px;
color :#fff;
margin-right :100px;
}
.header-input{
height :20px;
text-indent 10px;
outline :none;
width :300px;
}
</style>
Undolist.test.js
import { shallowMount } from '@vue/test-utils'
import UndoList from '../../components/UndoList.vue'
describe('Undolist.vue',()=>{
it('UndoList 參數爲[],count值爲0',()=>{
const wrapper = shallowMount(UndoList,{
propsData:{list:[]}
})
const countElem = wrapper.find('[data-test="count"]')
const ListTtems = wrapper.findAll('[data-test="item"]')
expect(countElem.text()).toEqual('0')
expect(ListTtems.length).toEqual(0)
})
it('UndoList 參數爲[1,2,3],count值爲3, 且列表有內容, 且存在刪除按鈕',()=>{
const wrapper = shallowMount(UndoList,{
propsData:{list:[1,2,3]}
})
const countElem = wrapper.find('[data-test="count"]')
const ListTtems = wrapper.findAll('[data-test="item"]')
const delBtns = wrapper.findAll('[data-test="delBtn"]')
expect(countElem.text()).toEqual('3')
expect(ListTtems.length).toEqual(3)
expect(delBtns.length).toEqual(3)
})
it('刪除按鈕被點擊時,向外觸發刪除事件',()=>{
const wrapper = shallowMount(UndoList,{
propsData:{list:[1,2,3]}
})
// 找到第2個刪除按鈕
const delBtns = wrapper.findAll('[data-test="delBtn"]').at(1)
delBtns.trigger('click')
expect(wrapper.emitted().delUndoItem).toBeTruthy()
})
})
undolist.vue
<template>
<div class="undolist">
<div class="undoList-title">
<p>正在進行</p>
<div data-test="count" class="count">{{list.length}}</div>
</div>
<ul class="item-wrap">
<li data-test="item"
class="item"
v-for="(item,index) in list"
:key="index"
>
{{item}}
<span class="delBtn" data-test="delBtn" @click="delUndolist(index)"> - </span>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'Undulist',
props: {
list: {
type: Array,
default: () => {
return []
}
}
},
data () {
return {
}
},
methods: {
delUndolist (data) {
this.$emit('delUndoItem', data)
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="stylus">
.undoList-title{
display : flex;
justify-content :space-between;
align-items center;
width :500px;
margin : 50px auto;
text-align center;
}
.undoList-title>p{
font-size :30px;
font-weight :bold;
}
.count{
background :#E6E6FA;
width :20px;
height 20px;
border-radius :50%;
color: #666;
font-size: 14px;
}
.item-wrap{
width :500px;
margin : 0 auto;
padding :0;
}
.item{
text-align center;
display : flex;
justify-content :space-between;
padding : 10px 20px;
box-sizing :border-box;
align-items center;
background :rgb(245,245,245);
margin-bottom :20px;
border-radius :5px;
border-left :5px solid #629A9C
}
.delBtn{
background :#ccc;
width :20px;
height 20px;
border-radius :50%;
color: #fff;
line-height :20px;
font-size: 14px;
}
</style>
BDD實戰示例
下列就是根據上述寫好的,來測試,這裏就示例一下,
it(`
1.用戶在輸入框中輸入了內容,
2.用戶會點擊回車按鈕
3.列表項應該增加用戶輸入內容的列表項
`,()=>{
const wrapper = mount(todoList)
const inputElem = wrapper.find('[data-test="input"]')
const content = "toDoList
inputElem.setValue(content)
inputElem.trigger('change')
inputElem.tigger('keyup.enter')
const listItems = wrapper.findAll('[data-test=item]')
expect(listItems.length).toBe(1)
expect(listItems.at(0).text()).toContain(content)
})
是否發現異常繁瑣,TDD開發模式更適用於開發,類似方法函數庫,對於數據的處理,對於這種顯示組件,更加推薦於
BDD的開發方式,這樣既有了測試,也不會增加過多的工作負擔,