H5W3
当前位置:H5W3 > 其他技术问题 > 正文

前端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

class Header extends Component {
render(){
return (
<div> <input data-test='input' /> </div>
)
}
}
 

3. Header.test.js

it('header input 初始化应为空', ()=>{
expect(inputElem).toHaveValue('')
})
 

4. Header.js

class Header extends Component {
constructor(props){
super(props);
this.state = {
value: ''
}
}
render(){
const {value} = this.state;
return (
<div> <input data-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

class Header extends Component {
handleInputChange = (e)=>{
this.setState({
value: e.targe.value
})
}
render(){
return (
<div> <input onChange={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 优势 — 代码质量高
单元测试 -- 测试覆盖率高 业务耦合度高
-- 代码量大 过于独立

  1. 先写测试再写代码
  2. 一般结合单元测试使用,是白盒测试
  3. 测试重点在代码
  4. 安全感低
  5. 速度快

函数库,UI组件库适合使用单元测试

业务代码更适合集成测试

二 BDD (Behavior Driven Developmen)基本流程

基于用于行为测试

  1. 先写代码在写测试
  2. 一般结合集成测试使用,是黑合测试
  3. 测试重点在UI(DOM)
  4. 安全感高
  5. 速度慢

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

export class TodoList extends Component {
//...
}
const mapDispatch = dispatch => ({
handleInputChange(value){
dispatch(actions.changeInputValue(value)
}
})
export default connect(mapState, mapDispatch)(Header);
 

TodoList.test.js

import {Provider} from 'react-redux';
import store from '../../store/createStore';
it('', ()=>{
const wrapper = mount(
<Provider store={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过程

  1. 快速新增一个测试
  2. 运行所有测试,发现最新的测试不能通过
  3. 做小小的改动
  4. 运行所有测试,且全部通过
  5. 重构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来让测试通过
  • 逐渐使工作代码一般化,用变量代替常量
  • 将工作逐步加入计划清单,而不是一次全部提出

测试驱动开发总体流程如下

  1. 写一个测试程序,考虑你希望实现的操作要如何在你的代码中体现出来。你是在写story。设想你希望拥有的接口interface。在story中包含任何你所能想象到的、计算出正确结果所必需的元素。
  2. 让测试程序运行。尽快让测试可运行是压倒一切的中心任务。如明显存在整洁、简单的解决方案,那就键入。如果这个方案需耗费1分钟,那把它记下来,再回到主要问题点来,即怎样才能让测试在几秒内就能运行通过。(这种偏离审美的举动是难以理解的,但只是暂时的)
  3. 编写合格的代码。回归正派的软件设计之路。消除先前的重复设计、使测试尽快运行通过。

我们最终目标是整洁可用的代码。首先解决目标的“可用”问题,,然后在解决“整洁”问题。

尽快使测试可运行的三条策略中的两条:

  • 伪实现 - 返回一个常量并逐渐用变量代替常量,直至伪实现成为真实实现代码
  • 显明实现Obvious Implementation - 将真实的实现代码键入

我经常交替使用这两种实现模式,如果一切顺利,且我知道该写些什么,我会采用显明实现。一旦测试没有通过,我会退回转而采用伪实现,重构直到得到正确的代码。当恢复自信时,再次开始显明实现

首先我们讨论系统应当是这样还是那样工作。一旦就系统的行为达成一致,就开始谈论如何用最好的办法来实现它。

为了让测试能尽快工作,我们大量使用了丑陋的方法。现在是收拾烂摊子的时候了。

待续重构笔记

本文地址:H5W3 » 前端TDD 与 BDD

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址