前端TDD 与 BDD
一 TDD 基本流程
1. Header.test.js
it('header 包含一个input框', ()=>{const wrapper = shallow(<Header/>)
const inputElem = findTestWrapper(wrapper, 'input')
expect(inputElem.length).toExist()
})
2.Header.js
classHeaderextendsComponent{render(){
return (
<div><inputdata-test='input' /></div>
)
}
}
3. Header.test.js
it('header input 初始化应为空', ()=>{expect(inputElem).toHaveValue('')
})
4. Header.js
classHeaderextendsComponent{constructor(props){
super(props);
this.state = {
value: ''
}
}
render(){
const {value} = this.state;
return (
<div><inputdata-test='input'value={value}/></div>
)
}
}
5. Header.test.js
it('header input 用户输入时,变化', ()=>{const userInput = 'learning jest';
inputElem.simulate('change', {
target: {value: userInput}
})
expect(wrapper).toHaveState({value: userInput})
expect(inputElem).toHaveValue(userInput)
})
6. Header.js
classHeaderextendsComponent{handleInputChange = (e)=>{
this.setState({
value: e.targe.value
})
}
render(){
return (
<div><inputonChange={this.handleInputChange}/></div>
)
}
}
keyCode
it('input 输入回车时,如input无内容,无操作'), ()=>{const fn = jest.fn()
wrapper.setState({value: ''})
inputElem.simulate('keyUp', {
keyCode: 13
})
expect(fn).not.toHaveBeenCalled()
}
it('input 输入回车时,如input有内容,函数被调用'), ()=>{
const fn = jest.fn()
wrapper.setState({value: 'learn react'})
inputElem.simulate('keyUp', {
keyCode: 13
})
expect(fn).toHaveBeenCalled()
}
TDD 优势 -- 代码质量高
单元测试 -- 测试覆盖率高 业务耦合度高
-- 代码量大 过于独立
- 先写测试再写代码
- 一般结合单元测试使用,是白盒测试
- 测试重点在代码
- 安全感低
- 速度快
函数库,UI组件库适合使用单元测试
业务代码更适合集成测试
二 BDD (Behavior Driven Developmen)基本流程
基于用于行为测试
- 先写代码在写测试
- 一般结合集成测试使用,是黑合测试
- 测试重点在UI(DOM)
- 安全感高
- 速度慢
tests/integration/TodoList.js
it('1. 输入内容
2. 点击回车
3. 列表中展示用户输入的内容项
', ()=>{
const wrapper = mount(<TodoList/>)
const inputElem = findTestWrapper(wrapper, 'header-input')
const content = "aaa";
inputElem.simulate('change', {
target: {value: content}
})
inputElem.simulate('keyUp', {
keyCode: 13
})
const listItem = findTestWrapper(wrapper, 'list-item');
expect(listItem.length).toEqual(1);
expect(listItem.text()).toContain(content);
})
01 使用BDD测试redux
TodoList.js
exportclassTodoListextendsComponent{//...
}
const mapDispatch = dispatch => ({
handleInputChange(value){
dispatch(actions.changeInputValue(value)
}
})
exportdefault connect(mapState, mapDispatch)(Header);
TodoList.test.js
import {Provider} from'react-redux';import store from'../../store/createStore';
it('', ()=>{
const wrapper = mount(
<Providerstore={store}><TodoList/></Provider>
)
const inputElem = findTestWrapper(wrapper, 'header-input')
const content = "aaa";
inputElem.simulate('change', {
target: {value: content}
})
inputElem.simulate('keyUp', {
keyCode: 13
})
const listItem = findTestWrapper(wrapper, 'list-item');
expect(listItem.length).toEqual(1);
expect(listItem.text()).toContain(content);
})
三 TDD 理论
TDD过程
- 快速新增一个测试
- 运行所有测试,发现最新的测试不能通过
- 做小小的改动
- 运行所有测试,且全部通过
- 重构refactor代码,以消除重复设计duplication,优化设计结构
小实战 多币种资金
假设我们有这样一个报表
为了变成一个多币种的报表,加上单位
思考哪些测试一旦通过,就能说明代码正确地计算出报表呢?
- 假设已给定汇率情况下,要能对两种币种的金额相加,并将结果转为某一种币种
- 要能将某一金额(每股股价)与某一个数(股数)相乘,并得到一个总金额
为此,我们要创建一个to-do list以提醒我们需要做哪些事情,保持注意力,告诉我们什么时候可完工。
当法郎与美元利率为2:1时,5美元+10法郎=10美元
5美元*2 = 10美元
将amount定义为私有
Dollar 有副作用吗?
钱数必须为整数?
首先我们从测试开始。从简单的开始,清单中第二个不过是实现乘法功能,就从它开始。
const amount = 10;
let amount = 5 * 2;
将5*2移至times()中
let amount;let times = ()=>{
amount = 5 * 2
};
如果你可以将代码分成一个个粒度比较小的任务,那么你自然可将它分得大小适当。但当你仅仅采用较大的步伐进行开发,那么你根本不会知道较小步伐是否合适。
let times = (amount)=>{amount = amount * 2
};
let times = (amount, multiplier)=>{amount = amount * multiplier
};
let times = (amount, multiplier)=>{amount *= multiplier
};
现在第一个测试已经完成,回顾一下, 我们做了以下工作
- 创建一个清单,列出我们所知道的需要让其运行通过的测试
- 通过一小段代码说明我们希望看到怎样的操作
- 暂时忽略细节问题
- 通过建立存根stub来让测试通过
- 逐渐使工作代码一般化,用变量代替常量
- 将工作逐步加入计划清单,而不是一次全部提出
测试驱动开发总体流程如下
- 写一个测试程序,考虑你希望实现的操作要如何在你的代码中体现出来。你是在写story。设想你希望拥有的接口interface。在story中包含任何你所能想象到的、计算出正确结果所必需的元素。
- 让测试程序运行。尽快让测试可运行是压倒一切的中心任务。如明显存在整洁、简单的解决方案,那就键入。如果这个方案需耗费1分钟,那把它记下来,再回到主要问题点来,即怎样才能让测试在几秒内就能运行通过。(这种偏离审美的举动是难以理解的,但只是暂时的)
- 编写合格的代码。回归正派的软件设计之路。消除先前的重复设计、使测试尽快运行通过。
我们最终目标是整洁可用的代码。首先解决目标的“可用”问题,,然后在解决“整洁”问题。
尽快使测试可运行的三条策略中的两条:
- 伪实现 - 返回一个常量并逐渐用变量代替常量,直至伪实现成为真实实现代码
- 显明实现Obvious Implementation - 将真实的实现代码键入
我经常交替使用这两种实现模式,如果一切顺利,且我知道该写些什么,我会采用显明实现。一旦测试没有通过,我会退回转而采用伪实现,重构直到得到正确的代码。当恢复自信时,再次开始显明实现
首先我们讨论系统应当是这样还是那样工作。一旦就系统的行为达成一致,就开始谈论如何用最好的办法来实现它。
为了让测试能尽快工作,我们大量使用了丑陋的方法。现在是收拾烂摊子的时候了。
待续重构笔记
以上是 前端TDD 与 BDD 的全部内容, 来源链接: utcz.com/a/28355.html