前端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 优势 -- 代码质量高

单元测试 -- 测试覆盖率高 业务耦合度高

-- 代码量大 过于独立

  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

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过程

  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 - 将真实的实现代码键入

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

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

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

待续重构笔记

以上是 前端TDD 与 BDD 的全部内容, 来源链接: utcz.com/a/28355.html

回到顶部