前端自动化测试-BDD-集成测试
Posted lin-fighting
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端自动化测试-BDD-集成测试相关的知识,希望对你有一定的参考价值。
何为BDD(Behavior Driven Development)
-
行为驱动的开发,是一种敏捷软件开发的技术,他鼓励软件项目中的开发者,QA和非技术人员或商业参与者之间的协作。
-
以上节TDD的代码为例子,将测试文件全部删除。从BDD的角度出发,开发人员不需要先写测试用例,直接编写代码即可。
-
编写完代码之后,第二步就是需要站在用户的角度,怎么去测试,测试的重点是什么?从用户的行为角度出发,编写我们的测试用例。因为用户行为是连续的,比如toDOList案例,用户希望在输入框输入内容按下回车后,list组件可以显示出来。他们需要不同组建的一个协同。
-
所以BDD一般是结合集成测试来开发。
-
还是以todoList案例,等我们编写玩开发代码,站在用户的角度出发,我希望,输入内容之后,按下回车,能在List里面看到我想要的内容。根据这个需求编写测试用例。
it(`
1. 用户输入输入框内容
2. 点击回车
3. 列表中显示输入的内容
`, () =>
// 多个组件,使用mount
const wrapper = mount(<TodoList />);
const inputElm = wrapper.find("[data-test='input']");
const inputValue = "用户输入了内容";
// 模拟用户第一个行为
inputElm.simulate("change",
target: value: inputValue ,
);
// 点击回车
inputElm.simulate("keyUp",
keyCode: 13,
);
// 列表中应该显示内容
const listItems = wrapper.find("[data-test='list-item']");
console.log('listItems', listItems.debug());
expect(listItems.length).toBe(1);
expect(listItems.text()).toBe(inputValue);
);
如上,因为涉及到多个组件,所以必须使用mount渲染,然后模拟用户输入内容,触发回车事件,然后判断列表中是不是真的多出一个list。
测试通过。
何为BDD?
通过简单的案例我们可以知道BDD,是以用户行为驱动的开发,
- 流程就是 先根据功能编写代码,等功能开发完毕之后,
- 站在用户的角度出发,去编写测试用例。
- Bdd一般结合集成测试。
- 而TDD则是以测试驱动的开发,先编写测试用例,再开发代码。
- TDD一般结合单元测试。主要是为每个组件的功能进行测试。
TDD & BDD
TDD:
- 先写测试再写代码
- 一般结合单元测试,是白盒测试
- 测试重点在代码
- 安全感第
- 速度快(shallow)
BDD: - 先写代码,再写测试
- 一般结合集成测试使用,是黑盒测试
- 测试重点在UI(DOM)
- 安全感高
- 速度慢(mount)
如何进行Redux的测试
如果使用TDD进行redux的测试,那么action ,reducer等等都需要编写测试用例,代码量大。也不能保证数据流正确。
所以得使用BDD来进行redux的测试比较好。
- 还是以toDOlist案例,把状态存储到redux去。
改造一下demo:
Header组件
function Headers( onAddItem : Props)
const dispatch = useDispatch();
const [value, setValue] = React.useState("");
return (
<div className="Header">
<input
className="header-input"
type="text"
data-test="input"
value=value
onChange=(e) =>
setValue(e.target.value);
onKeyUp=(e) =>
if (e.keyCode === 13)
dispatch( type: "dispatch", value );
setValue("");
else
return;
/>
</div>
);
list组件
function TodoList()
const items = useSelector(
(state: any) => ( items: state.todo.items ),
shallowEqual
);
return (
<div>
<Headers />
<div>------------</div>
items.map((ctem) =>
return (
<div data-test="list-item" key=ctem>
ctem
</div>
);
)
</div>
);
将状态存储到redux去。
然后重新编写刚才的测试用例
it(`
1. 用户输入输入框内容
2. 点击回车
3. 列表中显示输入的内容
`, () =>
// 多个组件,使用mount
const wrapper = mount(
<Provider store=store>
<TodoList />
</Provider>
);
const inputElm = wrapper.find("[data-test='input']");
const inputValue = "用户输入了内容";
// 模拟用户第一个行为
inputElm.simulate("change",
target: value: inputValue ,
);
// 点击回车
inputElm.simulate("keyUp",
keyCode: 13,
);
// 列表中应该显示内容
const listItems = wrapper.find("[data-test='list-item']");
console.log("listItems", listItems.debug());
expect(listItems.length).toBe(1);
expect(listItems.text()).toBe(inputValue);
);
这里需要跟外部一样,将store传进去给他。这里只是需要更改一下moutn的内容,其他东西根本不需要更改,这也是BDD的好处。
- 开发业务代码不用考虑测试用例
- 业务代码如果更改比如从state到redux,那么BDD测试用例是很少改动的,因为他不是基于代码的,而是基于用户角度,只要流程跑的通,就正确。(耦合度低)
- BDD驱动的集成测试,安全感较高,因为这个测试用例一通过,就表示这个功能是可以用的。而不会出现单元测试无法保证多个组件结合在一起的可用性。
- BDD的代码覆盖率会降低,但是影响不大。
异步代码的测试
- 还是以todoList为例
- 实现一个功能,在页面初始化的时候i请求数据,展示List。
useEffect(() =>
// 测试的时候我们不需要发送请求
axios.get("/xxx").then((res: any) =>
setItem((pre) => [...pre, ...res.data]);
);
, []);
功能写完编写测试用例
因为测试用例需要执行组件,所以模拟axios
__mocks__/axios.ts
export default
get(url)
if (url === "/xxx")
return new Promise((r) =>
r( data: ["test1", "test2"] );
);
,
;
当我们使用mount渲染组件的时候,里面遇到axios的地方就会去上面获取,如上,返回了test1 test2。
test(`1.用户打开页面
2. 展示接口返回的数据
`, (done) =>
const wrapper = mount(
<Provider store=store>
<TodoList />
</Provider>
);
// 异步需要等待
setTimeout(() =>
wrapper.update()
console.log(wrapper.debug())
const listItems = (wrapper as any).find("[data-test='list-item']");
expect(listItems.length).toBe(2);
done()
, 1000);
);
因为请求时异步的,所以我们需要等待一下才能判断,然后wrapper.update去更新dom,此时dom已经有了请求回来的数据。因为这里是异步,所以需要用到done。
看下更新后的dom:
第一次渲染
console.log
<Provider store=...>
<TodoList>
<div>
<Headers>
<div className="Header">
<input className="header-input" type="text" data-test="input" value="" onChange=[Function: onChange] onKeyUp=[Function: onKeyUp] />
</div>
</Headers>
<div>
------------
</div>
</div>
</TodoList>
</Provider>
没有List。
更新后:
<Provider store=...>
<TodoList>
<div>
<Headers>
<div className="Header">
<input className="header-input" type="text" data-test="input" value="" onChange=[Function: onChange] onKeyUp=[Function: onKeyUp] />
</div>
</Headers>
<div>
------------
</div>
<div data-test="list-item">
test1
</div>
<div data-test="list-item">
test2
</div>
</div>
</TodoList>
</Provider>
已经渲染出list了。
- setTimeout的异步怎么优化?不可能等待所有setTImeout完成的。
useEffect(() =>
setTimeout(() =>
axios
.get("/xxx")
.then((res: any) =>
console.log("res----------", res);
setItem((pre) => [...pre, ...res.data]);
)
.catch((err) =>
);
, 5000);
, []);
需要使用jests提供的setTImoeut
eforeEach(() =>
console.log(123123);
jest.useFakeTimers();
);
afterEach(() =>
jest.useRealTimers();
);
test(`1.用户打开页面
2. 展示接口返回的数据
`, (done) =>
// 将所有setTimoeut里面执行
const wrapper = mount(
<Provider store=store>
<TodoList />
</Provider>
);
jest.runAllTimers();
// 异步需要等待
setTimeout(() =>
wrapper.update();
const listItems = (wrapper as any).find("[data-test='list-item']");
expect(listItems.length).toBe(2);
done();
, 1000);
);
Test Suites: 2 passed, 2 total
Tests: 2 passed, 2 total
Snapshots: 1 obsolete, 0 total
Time: 6.854 s
这样就可以了。
总结:
- BDD就是先开发组件的功能,然后从用户角度出发,编写一套流程的测试用例,而不是单个单个的,当测试用例通过的时候,就表示这些组件结合起来的功能是完整的。
- 而TDD是先编写测试用例,(单元测试),然后再开发代码,可以保证每个组件的功能完整性,但是不能保证这些组件结合起来后的功能是否完整。
- BDD+集成测试对于业务代码是一个相对更好的方案,TDD+单元测试对于单独的函数库来说会更好。
前端自动化测试的优势
- 更好的代码组织,项目的可维护性增强
- 更小的bug出现概率,尤其回归测试的bug
- 修改工程质量差的项目,更加安全
以上是关于前端自动化测试-BDD-集成测试的主要内容,如果未能解决你的问题,请参考以下文章