在软件开发过程中,测试的重要性不可忽视。然而,传统的测试方式往往只注重系统的功能测试,而忽略了系统是否满足用户需求以及用户行为是否符合预期等因素。因此,在开发过程中采用行为驱动开发(BDD)的方式进行测试,可以更好地保证软件的质量和用户体验。
BDD 流程
BDD 是一种高效的测试方法,强调的是以用户需求和用户行为为中心进行测试。与传统的测试方法不同,BDD 提供了一种更加清晰的测试流程,如下所示:
- 定义 feature (功能): 定义要测试的系统的功能以及用户需求。
- 编写 scenario (场景): 定义使用这些功能的场景,这些场景以“Given-When-Then”形式呈现,形成一个用户故事。
- 实现 feature (功能实现): 根据场景中的需求,实现系统功能。
- 运行测试: 这里采用 Mocha.js 测试框架执行 BDD 测试用例。
Mocha.js
Mocha.js 是一个流行的测试框架,可以在 Node.js 和浏览器中运行,支持多种测试方式(如 TDD、BDD 等)。本文将以 BDD 的方式介绍 Mocha.js 的使用。
安装和使用 Mocha.js
首先,需要安装 Mocha.js。可以使用 npm 来安装:
$ npm install mocha --save-dev
接着,在项目根目录创建一个测试目录,并在测试目录中创建一个测试文件,例如 test.js
,然后写入如下测试代码:
-- -------------------- ---- ------- ---------------- ---------- - ------------------ ---------- - ---------- ------ -------- ----- -- ----- -------- ---------- - -------------------------- --- --- ---------- ------ -------- ----- -- ----- ------- -------- ---------- - --- --- - - -- --- -- - -- ------------------------------- - -- -- -- - --- --- --- ------------------ ---------- - ---------- ------ ------- ----- -- ----- -------- ---------- - ------------------------ --- --- --- ---------- ------ --- --- -------- ---- ------ --- ---------- ---------- ---------- - ------------------------- ------- --- --- ---
这是一个简单的测试文件,用于测试 JavaScript 中 Math 对象的 abs
和 pow
方法。这个测试文件定义了两个 feature,分别是 Math.abs()
和 Math.pow()
。其中 abs()
feature 定义了两个场景,一个是检查 abs(-1)
的返回值,一个是检查 abs({ x: -1, y: 2 })
的返回值;pow()
feature 定义了两个场景,一个是检查 pow(2, 3)
的返回值,一个是检查 pow(-2, 0.5)
的返回值。
这是一个 BDD 测试用例的典型例子,它使用了 Mocha.js 的 describe()
、it()
和 assert()
方法。其中,describe()
方法用于定义 feature 和 scenario,it()
方法用于定义具体的测试用例,assert()
方法用于判断测试结果是否符合预期。
运行测试
当测试用例准备好之后,可以使用 Mocha.js 进行测试。可以在命令行中执行 mocha
命令来运行测试,例如执行:
$ mocha test.js
这里的 test.js
是上面定义的测试文件,执行该命令会输出运行结果:
-- -------------------- ---- ------- ---- ------ - ------ ------ -------- ----- -- ----- ------ - ------ ------ -------- ----- -- ----- ------- ------ ------ - ------ ------ ------- ----- -- ----- ------ - ------ ------ --- --- -------- ---- ------ --- ---------- -------- - ------- -----
这里输出了测试的结果,其中 passing
表示测试通过的用例个数,ms
表示测试运行的时间。如果测试用例执行失败,则会输出具体的错误信息。
实战示例
下面将通过一个简单的实例来演示如何使用 BDD 流程和 Mocha.js 测试一个前端应用。
前端应用
我们要测试的是一个前端应用,这个应用提供了一个 TodoList 的功能,能够添加、修改、删除和查询 Todo 任务。这个应用的主要功能包括:
- 添加一个 to-do 任务。
- 编辑一个 to-do 任务。
- 删除一个 to-do 任务。
- 列出所有 to-do 任务。
有了这些功能之后,接下来我们需要编写测试用例,使用 BDD 流程和 Mocha.js 测试这个应用。
测试用例
Feature: 添加任务
作为一个使用者,我希望能够添加一个任务,以便能够记录我的 to-do 事项并进行管理。
场景 1: 添加一个新的任务。
Given 一个全新的任务 When 点击添加按钮时 Then 新的任务应该被添加到任务列表中
场景 2: 添加一个无效的任务。
Given 一个无效的任务 When 点击添加按钮时 Then 添加应该被拒绝,并且出现错误信息
Feature: 修改任务
作为一个使用者,我希望能够修改一个任务,以便我能够更好的管理我的 to-do 事项。
场景 1: 修改任务的标题。
Given 一个任务 When 点击该任务的编辑按钮时 And 打开编辑窗口,并修改任务的标题 And 点击保存按钮 Then 任务的标题应该被修改成功,并在任务列表中更新
场景 2: 修改任务的时间。
Given 一个任务 When 点击该任务的编辑按钮时 And 打开编辑窗口,并修改任务的时间 And 点击保存按钮 Then 任务的时间应该被修改成功,并在任务列表中更新
Feature: 删除任务
作为一个使用者,我希望能够删除一个任务,以便我能够更好的管理我的 to-do 事项。
场景 1: 删除一个任务。
Given 一个任务 When 点击该任务的删除按钮时 Then 任务应该被删除,并在任务列表中更新
场景 2: 删除多个任务。
Given 多个任务 When 选中其中多个任务,并点击删除按钮时 Then 所有选中的任务应该被删除,并在任务列表中更新
Feature: 列出所有任务
作为一个使用者,我希望能够查看所有的任务列表,以便我能够更好的管理我的 to-do 事项。
场景 1: 列出所有任务。
Given 多个任务 When 点击查看所有任务按钮时 Then 所有的任务列表应该被列出,并且包含所有的任务
场景2: 对任务进行排序。
Given 多个任务 When 点击排序按钮时 Then 所有的任务列表应该按照指定的顺序排序
实现
有了测试用例之后,接下来需要根据测试用例来实现应用。这里以 React.js 作为前端框架,采用 Redux 来管理数据,来实现一个简单的 TodoList 应用,供大家参考。
目录结构
-- -------------------- ---- ------- --------- --- ---- - --- -------- - - --- -------- - --- ----------- - - --- ------ - - --- --------- - - --- ------- - - --- ----------- - --- --------- - - --- -------- - - --- -------- - --- ------ - - --- ----------------- - --- -------- --- ------ - --- ------- - - --- --------- - - --- ------------- - --- ---------- - --- --------- --- ----- - --- ------- - - --- --------- - - --- ------------- - --- ---------- - --- --------- --- ------------- --- ----- - --- ------------ --- ------------
这个项目的目录结构比较简单,app/
目录下是应用的源代码,build/
目录下是开发版本的构建结果,dist/
目录下是生产版本的构建结果,test/
目录下是测试用例。
具体实现
在 app/
目录下,主要是 TodoList 应用的实现。这里采用了 React.js、Redux 和 ES6 语法等技术,具体代码实现如下。
App.js
-- -------------------- ---- ------- ------ ----- ---- -------- ------ - ------- - ---- -------------- ------ - -------- ----------- ---------- - ---- ------------- ------ ------ ---- ----------- ------ ---- ---- --------- ----- --- ------- --------------- - ------------------ - ------------- ---------- - - ----- ------ -- - --------- - ------ -- - ----------------------- -- ------------ - ---- -- - ------------------------ -- ------------ - ---- ----- -- - ----------------------- ------ -- ---------------- - ------ -- - --------------- ----- ----- --- -- -------- - ----- - ---- - - ----------- ----- - ----- - - ----------- ----- ------------- - ---- --- ----- - ----- - ---- --- -------- - ------------------- -- ---------------- - ------------------- -- ---------------- ------ - ---- ---------------------- ------- ---------------------- -- ----- --------------------- ---------------------------- ---------------------------- ------------------------------------ ------------------- -- ------ -- - - ----- --------------- - ------- -- - ------ - ------ ------------ -- -- ----- ------------------ - ---------- -- - ------ - ------ ------ -- - ------------------------ -- --------- ---- -- - ------------------------- -- --------- ---- ----- -- - ----------------------- ------- -- -- -- ------ ------- ------------------------ -------------------------
这里定义了一个 App
的 React 组件,该组件是整个应用的入口。在组件中,使用了 connect()
方法将组件和 Redux 状态管理器连接起来。组件中定义了多个处理逻辑的方法,如 handleAdd()、handleDelete() 和 handleUpdate() 等。
Header.js
-- -------------------- ---- ------- ------ ----- ---- -------- ------ ------- ---- ---------- ----- ------ ------- --------------- - ------------------ - ------------- ---------- - - ------ --- ----- --- ------- -- - ----------------- - --- -- - --------------- ------ -------------- --- -- ---------------- - --- -- - --------------- ----- -------------- --- -- ------------ - --- -- - ------------------- ----- - ------ ---- - - ----------- -- ------------- --- --- ------- ----- ---- - - --- ------------------- ------ ------ ----- ----- ---------- ------ -- ----------------------- --------------- ------ --- ----- --- ------ --- -- -------- - ----- - ------ ---- - - ----------- ------ - -------- ----------------- ----- ----------------------------- ---- --------------------- ----- ---- --------------------- ------ ----------- ---------------------------------- --------------------- ------------- --------------------------------- --------- -- ------ ---- --------------------- ------ ----------- ---------------------------------- ------------ -------------------------------- -- ------ ---- --------------------- ------- ------------- -------------- ------------- -- --------- ------ ------ ------- --------- -- - - ------ ------- -------
Header
组件是整个应用的头部组件,用于添加任务。在该组件中定义了一个表单,用于输入任务信息。当表单提交时,通过 onAdd
属性调用 App 组件中的 handleAdd()
方法来添加任务。
List.js
-- -------------------- ---- ------- ------ ----- ---- -------- ------ -------- ---- ------------- ----- ---- - ------- -- - ----- - ------ --------- --------- ------------- ------------ - - ------ ----- ---------------- - --- -- - ----- ---- - --------------- ------------------- -- ------ - ----- ---- ----------------------- ---- --------------------- ------------ ------------------- ------- ------------- --------------- --------------------- -- ------------ --- ----- - -------- - -- --- ----------- -------------------------- - -- --------- ------- ------------- --------------- --------------------- -- ------------ --- -------- - -------- - -- --- -------------- -------------------------- - --- --------- ------- ------------- --------------- --------------------- -- ------------ --- ----------- - -------- - -- --- ----------------- -------------------------- - --- --------- ------ ------ --- ----------------------- ----------------- -- - --------- ------------- ----------- ------------------- ------------------- -- --- ----- ------ -- -- ------ ------- -----
List
组件展示了所有的任务列表,包括已完成、未完成和全部三种类型。通过 onTypeChange()
方法来处理任务列表的类型变化,在渲染列表时根据类型进行过滤。List 组件中也通过 props 将 Delete 和 Update 事件分别传递到 ListItem 子组件中。
ListItem.js
-- -------------------- ---- ------- ------ ----- ---- -------- ----- -------- ------- --------------- - ------------------ - ------------- ---------- - - ------ ----------------- ----- ---------------- -------- ------ -- - ------------ - -- -- - ---------------------------------------- -- ---------- - -- -- - ------------------------- -- -- -------- ------------------- ---- -- ---------- - --- -- - ------------------- ----- - ------ ---- - - ----------- ----- - ---- - - ----------- ----- ------- - - -------- ------ ------ ----- ---- -- ---------------------------- --------- --------------- -------- ----- --- -- ------------ - --- -- - ------------------- --------------- -------- ------ ------ ---------------------- ----- --------------------- --- -- ----------------- - --- -- - --------------- ------ -------------- --- -- ---------------- - --- -- - --------------- ----- -------------- --- -- -------- - ----- - ---- - - ----------- ----- - ------ ----- ------- - - ----------- ------ - --- ---------------------------- --------- - - -- ------------------ ----------------- ---- --------------------- ------------ ------------------- ------- ------------- -------------- ------------ -------------------------- -- --------- ------- ------------- -------------- ----------- ---------------------------- -- --------- ------ --- - - - ----- --------------------------- ---- --------------------- ----- ---- --------------------- ------ ----------- ---------------------------------- ------------- --------------------------------- --------- -- ------ ---- --------------------- ------ ----------- ---------------------------------- ------------ -------------------------------- -- ------ ---- --------------------- ------- ------------- -------------- ------------- -- --------- ------- ------------- -------------- ---------------------- ---------------------------- -- --------- ------ ------ ------- -- ----- -- - - ------ ------- ---------
ListItem
组件是任务列表中的子项组件,每个子项对应一个任务。在该组件中,可以通过一个编辑按钮来编辑任务信息,包括标题和日期等属性。当点击保存按钮时,通过 onUpdate()
方法将新的任务信息传递给 App 组件进行更新。同时,在组件中也可以通过删除按钮将该任务删除。
测试用例实现
测试用例的代码在 test/
目录下,每个测试文件对应一组测试用例。这里只列出一个文件 todo.spec.js
的实现。
-- -------------------- ---- ------- ------ - ------ - ---- ------- ------ ----- ---- -------- ------ - ----------- - ---- -------- ------ - -------- - ---- -------------- ------ -------- ---- ------------ ------ --- ---- ------------------------ ------ ------- ---- ------------------------ ------------------ ---- ------- -- -- - --- ------ --- ---------- --------- -- - --------- - ------------------------------ ------------------------------------- ----- - --------------------- --- -------- -- - ------------------------------------- --------- - ----- ----- - ----- --- ----- ------ - -- -- - ---------------- --------- -------------- ---- -- ------------ --------- -- -- ----- ------- - ------- ----- -- - ---------------- ----- ----------- -------- - ------ ------ ----- ----- -- --- -- ----- ---------- - ---- -- - ---------------- ----- -------------- -------- - --- --- -- --- -- ----- ---------- - ---- ----- -- - ---------------- ----- -------------- -------- - --- --- ----- ----- -- --- -- ------------- -- - ---------------- -------------- ---------------- -------------- ---------------- -------------- ---------------- -------------- --- ------------ -- - ---------------- ----- -------------- --- --- ------------------ ------ -- -- - ------------------------------ -- -- - --------- ----- ----- - ------- ----- ---- - ------------- ----- ------ - ---------------------------------- ----- ---------- - ----------------------------------------- ----- --------- - ----------------------------------------- ----- ------ - -------------------------------------------- ---------------- - ------ --------------- - ----- --------------- ----- --- - ------------------------------------------------- ----- ------ - ----------------- -- -------------------------------------------------------- ----- ----- - ----------------- -- --------------------------------------------------------- ------------------------------- -------- -------- -------- -------- ----------------------------------- ------------- ------------- ------------- ------- --- ------------------------------- -- -- - --------- ----- ----- - --- ----- ---- - ------------- ----- ------ - ---------------------------------- ----- ---------- - ----------------------------------------- ----- --------- - ----------------------------------------- ----- ------ - -------------------------------------------- ---------------- - ------ --------------- - ----- --------------- ----- --- - ------------------------------------------------- ----- ------ - ----------------- -- -------------------------------------------------------- ----- ------------- - ---------- ------------------------------- -------- -------- ---------- --------------------------------------- ------------------------------------- ------------------------------------------------------------------- --- --- ------------------ ------ -- -- - ------------------------------------ -- -- - --------- ----- -- - ----------------------------- ----- ----- - --------- ----- -------- - ---------------------------------------------------- ----- ---------- - ---------------------------------------------------------- ------------------- ----- ---------- - ------------------------------------------- ----- --------- - ------------------------------------------- ----- ---------- - ---------------------------------------------- ---------------- - ------ ------------------- ----- --------------- - ---------------------------------------------------- ----- ------------ - -------------------------------------------------------------------- ----------------------------------- --- ------------------------------------ -- -- - --------- ----- -- - ----------------------------- ----- ---- - ------------- ----- -------- - ---------------------------------------------------- ----- ---------- - ---------------------------------------------------------- ------------------- ----- ---------- - ------------------------------------------- ----- --------- - ------------------------------------------- ----- ---------- - ---------------------------------------------- --------------- - ----- ------------------- ----- --------------- - ---------------------------------------------------- ----- ----------- - --------------------------------------------------------------------- --------------------------------- --- --- ------------------ ------ -- -- - -------------------------------- -- -- - --------- ----- -- - ----------------------------- ----- -------- - ---------------------------------------------------- ----- ------------ - --------------------------------------------------------- --------------------- ----- --- - ------------------------------------------------- ----- --- - ----------------- -- ------------------- ------------------------------- --- ----------------------------------- -- -- - --------- ----- --- - --------------------------------- -- --------- ----- --------- - ----------------------------------------------- ----- ---------- - ----------------------- -- ---------------------------------------- -- ----- ------------ - --------------------------------------------- ---------------------- ---------------------- --------------------- ----- --- - ------------------------------------------------- ----- ---------- - ----------------- -- ------------------- ------------------------------------------ ------------------------------------------ -------------------------------------- -------------------------------------- --- --- ------------------ -------- -- -- - ----------------------------- -- -- - --------- ----- --- - ------------------------------------------------- ----- ------ - ----------------- -- -------------------------------------------------------- ------------------------------- -------- -------- ---------- --- --------------------------------- -- -- - --------- ----- ------ - ---------------------------------- ----- ---------- - ----------------------------------------- ----- --------- - ----------------------------------------- ----- ------ - -------------------------------------------- ---------------- - ---------- --------------- - ------------- --------------- ---------------- - ---------- --------------- - ------------- --------------- ---------------- - ---------- --------------- - ------------- --------------- ----- ------ - -------------------------------------------------- --------------- ----- --- - ------------------------------------------------- ----- ------ - ----------------- -- -------------------------------------------------------- ------------------------------- -------- ---------- ------------ --- --------------------------------- -- -- - --------- ----- -- - ----------------------------- ----- -------- - ---------------------------------------------------- ----- ---------- - ---------------------------------------------------------- ------------------- ----- ---------- - ---------------------------------------------- ----- ----------------- - ----------------------------------------------- -------------------------- ------------------- ----- ------ - ----------------------------------------------------- --------------- ----- --- - ------------------------------------------------- ----- ------ - ----------------- -- -------------------------------------------------------- --------------------------------- --- -------------------------------- -- -- - --------- ----- ------ - ---------------------------------- ----- ---------- - ----------------------------------------- ----- --------- - ----------------------------------------- ----- ------ - -------------------------------------------- ---------------- - -------- --------------- - ------------- --------------- ---------------- - -------- --------------- - ------------- --------------- ---------------- - -------- --------------- - ------------- --------------- ----- ------ - ----------------------------------------------- --------------- ----- --- - ------------------------------------------------- ----- -------------- - --------- -------- -------- -------- -------- -------- --------- ----- ------ - ----------------- -- -------------------------------------------------------- -------------------------------------- --- --- ---
这里使用了 Mocha.js 的测试框架,以及 Chai.js 的断言库和 Sinon.js 的桩件和间谍。在测试文件中,包含了添加任务、修改任务、删除任务和列出所有任务等测试用例,测试的具体内容在代码中有详细注释说明。
结论
使用 BDD 流程和 Mocha.js 进行测试,可以有效提高系统的质量和用户体验。通过定义清晰的 feature 和 scenario,以及编写详细的测试用例进行测试,可以保证系统的正确性和稳定性。通过本文中的实战演示,可以帮助读者更好地理解和应用 BDD 流程和 Mocha.js 进行前端应用的测试。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/672adecbddd3a70eb6d10774