使用 BDD(行为驱动开发)流程和 Mocha.js 测试应用

阅读时长 38 分钟读完

在软件开发过程中,测试的重要性不可忽视。然而,传统的测试方式往往只注重系统的功能测试,而忽略了系统是否满足用户需求以及用户行为是否符合预期等因素。因此,在开发过程中采用行为驱动开发(BDD)的方式进行测试,可以更好地保证软件的质量和用户体验。

BDD 流程

BDD 是一种高效的测试方法,强调的是以用户需求和用户行为为中心进行测试。与传统的测试方法不同,BDD 提供了一种更加清晰的测试流程,如下所示:

  1. 定义 feature (功能): 定义要测试的系统的功能以及用户需求。
  2. 编写 scenario (场景): 定义使用这些功能的场景,这些场景以“Given-When-Then”形式呈现,形成一个用户故事。
  3. 实现 feature (功能实现): 根据场景中的需求,实现系统功能。
  4. 运行测试: 这里采用 Mocha.js 测试框架执行 BDD 测试用例。

Mocha.js

Mocha.js 是一个流行的测试框架,可以在 Node.js 和浏览器中运行,支持多种测试方式(如 TDD、BDD 等)。本文将以 BDD 的方式介绍 Mocha.js 的使用。

安装和使用 Mocha.js

首先,需要安装 Mocha.js。可以使用 npm 来安装:

接着,在项目根目录创建一个测试目录,并在测试目录中创建一个测试文件,例如 test.js,然后写入如下测试代码:

-- -------------------- ---- -------
---------------- ---------- -
  ------------------ ---------- -
    ---------- ------ -------- ----- -- ----- -------- ---------- -
      -------------------------- ---
    ---
    
    ---------- ------ -------- ----- -- ----- ------- -------- ---------- -
      --- --- - - -- --- -- - --
      ------------------------------- - -- -- -- - ---
    ---
  ---
  
  ------------------ ---------- -
    ---------- ------ ------- ----- -- ----- -------- ---------- -
      ------------------------ --- ---
    ---
    
    ---------- ------ --- --- -------- ---- ------ --- ---------- ---------- ---------- -
      ------------------------- -------
    ---
  ---
---

这是一个简单的测试文件,用于测试 JavaScript 中 Math 对象的 abspow 方法。这个测试文件定义了两个 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 命令来运行测试,例如执行:

这里的 test.js 是上面定义的测试文件,执行该命令会输出运行结果:

-- -------------------- ---- -------
  ----
    ------
      - ------ ------ -------- ----- -- ----- ------
      - ------ ------ -------- ----- -- ----- ------- ------
    ------
      - ------ ------ ------- ----- -- ----- ------
      - ------ ------ --- --- -------- ---- ------ --- ---------- --------


  - ------- -----

这里输出了测试的结果,其中 passing 表示测试通过的用例个数,ms 表示测试运行的时间。如果测试用例执行失败,则会输出具体的错误信息。

实战示例

下面将通过一个简单的实例来演示如何使用 BDD 流程和 Mocha.js 测试一个前端应用。

前端应用

我们要测试的是一个前端应用,这个应用提供了一个 TodoList 的功能,能够添加、修改、删除和查询 Todo 任务。这个应用的主要功能包括:

  1. 添加一个 to-do 任务。
  2. 编辑一个 to-do 任务。
  3. 删除一个 to-do 任务。
  4. 列出所有 to-do 任务。

有了这些功能之后,接下来我们需要编写测试用例,使用 BDD 流程和 Mocha.js 测试这个应用。

测试用例

Feature: 添加任务

作为一个使用者,我希望能够添加一个任务,以便能够记录我的 to-do 事项并进行管理。

场景 1: 添加一个新的任务。

场景 2: 添加一个无效的任务。

Feature: 修改任务

作为一个使用者,我希望能够修改一个任务,以便我能够更好的管理我的 to-do 事项。

场景 1: 修改任务的标题。

场景 2: 修改任务的时间。

Feature: 删除任务

作为一个使用者,我希望能够删除一个任务,以便我能够更好的管理我的 to-do 事项。

场景 1: 删除一个任务。

场景 2: 删除多个任务。

Feature: 列出所有任务

作为一个使用者,我希望能够查看所有的任务列表,以便我能够更好的管理我的 to-do 事项。

场景 1: 列出所有任务。

场景2: 对任务进行排序。

实现

有了测试用例之后,接下来需要根据测试用例来实现应用。这里以 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

纠错
反馈