{"id":26684227,"url":"https://github.com/johnapache/mocha-usage-doc","last_synced_at":"2026-04-19T14:33:24.157Z","repository":{"id":47976607,"uuid":"203829546","full_name":"JohnApache/mocha-usage-doc","owner":"JohnApache","description":"基于Mocha的 测试框架体系搭建","archived":false,"fork":false,"pushed_at":"2023-01-04T07:53:13.000Z","size":194,"stargazers_count":1,"open_issues_count":7,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-18T06:02:59.282Z","etag":null,"topics":["chai","istanbul","mocha","mochawesome","nyc"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JohnApache.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-08-22T16:06:37.000Z","updated_at":"2019-09-17T08:08:38.000Z","dependencies_parsed_at":"2023-02-02T04:01:11.795Z","dependency_job_id":null,"html_url":"https://github.com/JohnApache/mocha-usage-doc","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohnApache%2Fmocha-usage-doc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohnApache%2Fmocha-usage-doc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohnApache%2Fmocha-usage-doc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohnApache%2Fmocha-usage-doc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JohnApache","download_url":"https://codeload.github.com/JohnApache/mocha-usage-doc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245623077,"owners_count":20645681,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chai","istanbul","mocha","mochawesome","nyc"],"created_at":"2025-03-26T09:19:22.668Z","updated_at":"2026-04-19T14:33:24.122Z","avatar_url":"https://github.com/JohnApache.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 基于Mocha的 测试框架体系搭建\n\n## 前言\n\u003e 我这里要介绍的内容不是仅仅如何使用Mocha 测试框架编写测试代码，我想整理的是基于Mocha的一个测试框架体系搭建，它包括Chai断言库的使用，Istanbul覆盖率测试，以及输出测试报告等等内容\n\n## Mocha 使用教程\n\u003e 首先引用官方的一句话，Mocha是一个在Node.js和浏览器上运行的功能丰富的JavaScript测试框架，它可以使异步测试变得简单而有趣。Mocha测试以串行方式运行，允许灵活准确的报告，同时将未捕获的异常映射到正确的测试用例。\n\n### Mocha的测试用例基本语法\n首先我们需要安装mocha\n```shell\nnpm install mocha --save-dev\n```\nmocha本身不支持es import/export等语法，这里使用@babel/register, mocha 运行的时候添加 --require @babel/register 就可以让mocha 支持es 语法\n```shell\nnpm install @babel/core @babel/preset-env @babel/register --save-dev\n```\n并在根目录创建babel.config.js\n```js\n// babel.config.js\nmodule.exports = {\n    presets: [\n        '@babel/env'\n    ]\n}\n```\n在package.json里面添加test命令\n```json\n{\n    \"scripts\": {\n        \"test\": \"mocha  --require @babel/register\"\n    },\n}\n```\n现在你可以通过 **npm run test** 运行 es 代码了\n这里先展示一个较为完整的Mocha demo\n创建需测试的代码文件\n```js\n// src/index.js\nconst isNumber = (num) =\u003e {\n    return typeof num === 'number';\n}\n\nexport const add = (...nums) =\u003e {\n    if(!nums.every(n =\u003e isNumber(n))) \n        throw new TypeError('params type need a number');\n    return nums.reduce((prev, cur) =\u003e {\n        return prev + cur;\n    }, 0)\n}\n```\n创建测试用例文件\n```js\n// test/index.test.js\nimport {add} from '../src/index';\nimport assert from 'assert';\ndescribe('add()方法测试', () =\u003e {\n    it.only('add方法的参数必须都是数字，否则会抛出异常', () =\u003e {\n        assert.throws(add.bind(null, '3'), TypeError);\n    });\n\n    it.only('add方法不传值的时候返回的是0', () =\u003e {\n        assert.equal(add(), 0);\n    });\n\n    it('这个测试用例也不会执行');\n\n    it.skip('这个测试用例不会执行，直接通过');\n})\n```\n#### 常用语法Api介绍\n+ **describe**\n    一个describe包含的测试用例块，称为\"测试套件\"(test suite)，它表示一组相关的测试用例集合。它是一个函数，第一个参数是测试套件的名称，描述这一组测试的主题，第二个参数是一个实际执行的函数。\n    ```js\n    describe('add()方法测试', () =\u003e {\n        //... 测试用例\n    })\n    ```\n+ **it**\n    it 表示的是一个测试用例(test case)，它往往表示的是一个具体功能的单元测试，是测试的最小单位。it也是一个函数，它的第一个函数表示测试用例的具体名称，第二个参数是一个执行函数，它可以接受一个参数done方法，异步代码测试就是依赖于这个done实现的\n    ```js\n     describe('add()方法测试', () =\u003e {\n         it('add方法的参数必须都是数字，否则会抛出异常', () =\u003e {\n            assert.throws(add.bind(null, '3'), TypeError);\n        });\n    });\n    ```\n+ **done**\n    mocha执行所有的测试用例都是同步执行的，当测试异步代码的时候，可以通过在it测试用例执行函数中，接收一个done函数，然后，在异步操作的结束阶段执行done，这样mocha 就可以实现等待异步代码测试了。当然 ，在我们使用了@babel/register之后，可以让mocha 支持async/await ，有了这个api 可以不通过done 函数就实现等待异步代码测试\n     ```js\n     describe('异步方法测试', () =\u003e {\n         it('setTimeOut()', (done) =\u003e {\n            setTimeOut(() =\u003e {\n               assert.throws(add.bind(null, '3'), TypeError);\n            }, 1000)\n        });\n     });   \n    ```\n    \u003e 注意mocha 的一个测试用例的默认等待时间最多是 2000毫秒，如果需要等待很长时间 可以手动传入mocha 命令行参数 -t或--timeout参数，来修改默认最长等待时间\n    ```shell\n    npx mocha -t 4000\n    ```\n+ **only**\n    only是一个静态属性，在describe方法 和it方法上面都有挂载这个静态方法。\n    - 当使用describe.only() 编写测试套件的时候，其他的test suite 都不会执行，只有该suite下面的 测试用例会执行，多个only describe是一个并集，所有only的describe都会执行\n    - 当使用it.only() 编写测试用例的时候，在该test suite下面的其他test case 都不会执行，但不会影响其他test suite，多个only it 是一个并集执行 \n    ```js\n     describe.only('会执行的测试套件', () =\u003e {\n        it.only('会执行的测试用例', () =\u003e {\n            assert.throws(add.bind(null, '3'), TypeError);\n        });\n        it.only('忽略的测试用例');\n     }); \n     describe('忽略的测试套件', () =\u003e {\n        // test cases...\n     }); \n    ```\n+ **skip**\n    skip也是describe和it 方法的静态属性，同时也是describe 和 it的执行函数this上下文实例方法.\n    \u003e 注意：由于是this的实例方法，当需要使用this.skip的时候，执行函数不能是箭头函数\n     - 当使用it.skip() 编写的测试套件会跳过当前test suite执行，多个skip describe是一个并集，所有skip的describe mocha都只会跳过执行\n    - 当使用it.skip() 编写的测试用例会跳过执行，不会影响其他test case正常执行，多个skip it 是一个并集执行 \n    -  当使用this.skip() 实例方法可以出现在describe和it 的实际执行函数中，它会跳过当前的测试，我们在编写测试代码的时候可以 通过判断环境决定 测试用例是否跳过，相比静态方法 更加灵活\n    ```js\n     describe('会执行的测试套件', () =\u003e {\n        it('会执行的测试用例', () =\u003e {\n            assert.throws(add.bind(null, '3'), TypeError);\n        });\n        it.skip('跳过的测试用例');\n        it('这个测试用例有概率会跳过',function() {\n            if(Math.random() \u003e 0.5) {\n                this.skip();// 实例方法不能使用箭头函数！！！\n            }else {\n                assert.equal(1, 1);\n            }\n        });\n     }); \n     describe.skip('跳过的测试套件', () =\u003e {\n        // test cases...\n     }); \n    ```\n+ **timeout**\n    timeout是describe的上下文实例方法，可以用于针对当前套件所有测试用例延迟等待的效果.当使用该方法的时候，describe 的执行函数不能是箭头函数。默认mocha的最大等待时间是2000ms，当需要针对个别测试套件 做特殊处理，可以使用这个方法只针对该套件修改等待时间\n    ```js\n    describe('延迟执行测试套件', function() {\n        this.timeout(6000);\n        it('延迟测试用例', (done) =\u003e {\n            setTimeout(done, 5000)\n        })\n    })\n    ```\n    \u003e 注意：当this.timeout(0)会套件内的所有禁用等待 \n       \n### Mocha的Hooks\nMocha的Hooks主要有4个，他们的执行顺序分别是 \nbefore =\u003e beforeEach =\u003e afterEach =\u003e after，Mocha的hooks是放在 每个 测试套件 describe 结构里的，他的执行时机是一个suite下的 每个测试用例执行前和执行后 的钩子\n+ **before** 这个hooks是在同一个套件suite下，所有的测试用例case执行之前 执行的钩子函数，只执行一次。\n+ **beforeEach** 这个钩子同before 一样在该套件下的测试用例case执行之前执行，但是不同的是beforeEach 会在每次执行 test case 前都会执行一次。\n+ **afterEach** 该钩子函数执行时机是在该测试套件下的每一个测试用例 test case 执行后都会执行一次。\n+ **after** 同before 相反，在该套件所有test case 执行完毕后才会执行的钩子函数，只执行一次\n\u003e mocha钩子函数经常用来在每个测试用例执行之前初始化数据用的。\n\u003e 每个hooks都是一个函数，第一个参数还可以接收一个描述字符串\n```js\ndescribe('Array Test', () =\u003e {\n    let sourceArr;\n    before('Array Test Init', () =\u003e {\n        console.log('最先执行，且只执行一次');\n    })\n    beforeEach('Every Test Case', aysnc () =\u003e {\n        // 每个测试用例执行前都会执行一次\n        sourceArr = Mock.mockArray();\n    })\n    afterEach(() =\u003e {\n        // 每个测试用例执行完都会执行一次\n        sourceArr = [];\n    })\n    after(() =\u003e {\n        console.log('最后执行，且只执行一次');\n    })\n    \n    it('这是一个测试用例', () =\u003e {\n       // do array test\n    })\n    \n    it('这是另一个测试用例', () =\u003e {\n       // do array test\n    })\n})\n```\n\u003e 注意：Mocha的hooks也支持异步操作 执行函数接收done 函数 或者 async/await\n```js\ndescribe('Array Test', () =\u003e {\n    let requestData;\n    before(async () =\u003e {\n        requestData = await getSyncData()\n    })\n    \n    beforeEach('sync hooks', (done) =\u003e {\n        setTimeOut(() =\u003e {\n            done();\n        }, 1000)\n    })\n)}\n```\n\u003e 最后有一个注意点，mocha的hooks 一般是放在describe 结构体中的，但是也可以放在describe结构体之外，当放在最外层的时候，钩子会作用于所有测试套件的测试用例之前或之后执行\n### Mocha的命令行配置\n这里只介绍几个常用的mocha 命令\n+ **mocha [test path]** \n    执行需要测试的文件路径匹配字符串,也可以传入多个路径\n    ```shell\n    npx mocha test/*.test.js spec/*.spec.js\n    ```\n+ **--ignore**    \n    忽略执行指定路径的测试文件\n    ```shell\n    npx mocha --ignore spec/some.spec.js\n    ```\n+ **--full-trace**    \n    mocha 完整的堆栈跟踪，方便调试\n+ **--reporter**\n    mocha 测试结果输出报告,默认报告器为spec，也可以使用第三方报告器，推荐mochawesome, \n    首先需要安装\n    ```shell\n    npm install mochawesome --save-dev\n    ```\n    然后运行很简单，--reporter 加上这个报告器即可\n    ```shell\n    npx mocha --reporter mochawesome\n    ```\n    还有dot, tap, nyan, landing, list, progress 等等内置reporter模式\n    \n+ **--timeout** \n    设置全局延迟时间，同this.timeout效果相仿，但是作用于全局\n    ```shell\n    npx mocha --timeout 5000\n    ```\n+ **--extension**    \n    定义什么样后缀的文件可以被加载当作测试文件测试，可以配合watch recursive等命令使用,也可以支持多个\n    ```shell\n    npx mocha --extention js --extention mjs\n    ```\n+ **--recursive** \n    这个命令参数用来让mocha测试文件的时候会递归子目录查找所有的测试文件，配合extension\n    ```shell\n    npx mocha --extention js --recursive\n    ```\n+ **--watch**\n    监视文件发生变化会重新运行mocha test，可以配合extension 取消监听某些文件的变化\n    ```shell\n    npx mocha --extention js --watch\n    ```\n+ **--grep,-g** \n    后面传入regexp，用于搜索所有测试套件 describe的标题或者 测试用例 it 的标题描述 中包含指定 regexp内容的 测试用例，并将只运行 符合这一部分的匹配的测试用例\n    ```shell\n    npx mocha --grep Get\n    ```\n+ **--growl** \n    该命令行参数用于让mocha的测试结果显示可以在桌面显示，感觉没啥子卵用, 该命令行参数需要安装 growl平台的一些软件.\n    安装方式 for MacOs,其他平台安装方式可以查看[growl](https://github.com/mochajs/mocha/wiki/Growl-Notifications)\n    ```shell\n    sudo gem install terminal-notifier\n    npm install growl\n    ```\n    运行mocha\n    ```shell\n    npx mocha --growl\n    ```\n## 断言库 Chai 使用教程\n\u003e 引用官方的一句话 chai是一个TDD(测试驱动开发)/BDD(行为驱动开发) 双模驱动的测试断言库, 这句话说的莫名其妙\n+ TDD（Test-Driven Development测试驱动开发）\n    测试先于编写代码的思想用于指导软件开发\n+ BDD（Behavior Driven Development行为驱动开发） \n    是一种敏捷软件开发的技术，它鼓励软件项目中的开发者、QA和非技术人员或商业参与者之间的协作\n    \n```shell\nnpm install chai --save-dev\n```\n### Chai断言库的基本语法\n#### BDD \nBDD 模式的断言库 有两种，expect, should。两者都使用相同的可链接语言来构造断言，但它们在最初构造断言的方式上有所不同\n+ expect 使用构造函数来创建断言对象实例\n+ should使用Object.prototype提供一个getter方法来实现，不兼容IE, 一般建议使用expect\n```shell\nconst chai = require('chai');\nconst expect = chai.expect;\nconst should = expect.should();\n```\nexpect/should 都支持链式调用语言链，为了提高断言的可阅读性。\n语言链有\n+ to\n+ be\n+ been\n+ is\n+ that\n+ which\n+ and\n+ has\n+ have\n+ with\n+ at\n+ of\n+ same\n+ but\n+ does\n \n\u003e expect/should常用api 请参考 [chai BDD](https://www.chaijs.com/api/bdd/)\n#### TDD \nTDD模式的断言库只有一种 assert，该assert 和nodejs 的断言库十分相似，但是chai的assert 在node的assert 基础上提供了更多的api语法糖，方便编写测试代码\n\u003e assert 常用api 请参考 [chai TDD](https://www.chaijs.com/api/assert/)\n\nchai的相关api是在太多，不需要刻意的整理 ，需要的时候查阅下即可\n\n\n## Istanbul使用教程\n\u003e 引用官方一句话，istanbul使用行计数器检测您的ES5和ES2015 + JavaScript代码，以便您可以跟踪单元测试运行代码库的情况。\n\n\u003e istanbul的 **nyc 命令行客户端** 适用于大多数JavaScript测试框架： tap， mocha， AVA等。\n\n+ 安装方法\n```shell\nnpm install nyc --save-dev\n```\n+ 使用方法\n终端命令行使用\n```shell\nnpx nyc mocha\n```\npackage.json script\n```json\n{\n  \"scripts\": {\n    \"test\": \"nyc mocha\"\n  }\n}\n```\n### babel-plugin-istanbul 插件介绍\n如果需要完美支持es6/es2015+的代码测试，还需要安装babel插件 [babel-plugin-istanbul](https://www.npmjs.com/package/babel-plugin-istanbul)\n```shell\nnpm install babel-plugin-istanbul --save-dev\n```\n在babel的配置文件中添加 istanbul babel插件的引用\n```js\n//babel.config.js\nmodule.exports = {\n    presets: [\n        '@babel/env'\n    ],\n    env: {\n        plugins:  [\n            \"istanbul\", \n            {\n                exclude: [\n                    '**/*.test.js', \n                    '*\\*/*.spec.js' \n                ],\n                useInlineSourceMaps: false\n            }\n        ]\n    },\n    plugins: [\n        [\n            '@babel/plugin-transform-runtime', {\n                corejs: 3\n            }\n        ]\n    ]\n}\n```\n该插件提供了几个常用的可配置选项，方便正确的覆盖率测试结果\n+ exclude \n    一般情况下，我们不希望测试文件记录到覆盖率测试中，可以通过这个配置选项 忽略 指定的测试文件\n\n+ useInlineSourceMaps\n    插件默认情况下会生成sourcemap 映射附加到已经测试的代码，以便代码覆盖率可以重新映射回原始源。这个可能会是内存密集型行为，可以设置为false 取消 生成sourcemap\n\u003e 经实验验证，该插件 并不适用于 @babel/register \n\n### istanbul 的测试结果 reporter\nnyc 提供了命令行参数，支持多种reporter 输出覆盖率测试结果,多个reporter 可以共存，用--reporter=value 形式提供多个reporter;\n```shell\nnpx nyc --reporter=lcov --reporter=text mocha --reporter=mochawesome\n```\n\n## 基于mocha的测试框架整体搭建\n创建package.json的script命令\n```json\n{\n     \"scripts\": {\n        \"test\": \"nyc --reporter=lcov --reporter=text mocha --require @babel/register --reporter=mochawesome --recursive\"\n      },\n}\n```\n创建babel.config.js文件, 由于使用的@babel/register，不需要安装过多的插件\n```js\n// babel.config.js\nmodule.exports = {\n    presets: [\n        '@babel/env'\n    ],\n}\n```\n创建两个需要测试的源文件\n```js\n// src/index.js\nconst isNumber = (num) =\u003e {\n    return typeof num === 'number';\n}\n\nexport const add = (...nums) =\u003e {\n    if(!nums.every(n =\u003e isNumber(n))) throw new TypeError('params type need a number');\n    return nums.reduce((prev, cur) =\u003e {\n        return prev + cur;\n    }, 0)\n}\n```\n```js\nexport const isArray = (target) =\u003e {\n\treturn Object.prototype.toString.call(target) === '[object Array]';\n};\n```\n\n创建对应的测试文件\n```js\n// test/index.test.js\nimport {add} from '../src/index';\nimport {expect} from 'chai';\ndescribe('add()方法测试', function() {\n    this.timeout(5000)\n    it('add方法的参数必须都是数字，否则会抛出异常', () =\u003e {\n        expect(add.bind(null, '3')).to.throw(TypeError)\n    });\n\n    it('add方法不传值的时候返回的是0', () =\u003e {\n        expect(add()).to.equal(0)\n    });\n\n    it('add方法可以接收多个值的求和结果', () =\u003e {\n        expect(add(1,2,3,4)).to.equal(10);\n    });\n\n    it('这个测试用例有概率会执行', function() {\n        if(Math.random() \u003e 0.5) {\n            this.skip();\n        }else {\n            expect(add()).to.equal(0);\n        }\n    });\n\n    it.skip('这个测试用例不会执行，直接通过');\n\n    it('该方法延迟执行', (done) =\u003e {\n        setTimeout(() =\u003e {\n            expect(add(1,2,3,4)).to.equal(10)\n            done();\n        }, 4500);\n    });\n\n})\n```\n```js\n// test/other.test.js\nimport {isArray} from '../src/other';\nimport {expect} from 'chai';\ndescribe('isArray()方法测试', function() {\n    it('isArray()返回值类型是Boolean', () =\u003e {\n        expect(isArray()).to.be.a('boolean');\n    });\n\n    it('isArray()可以测试数据是否是数组', () =\u003e {\n        expect(isArray(3)).to.be.false;\n        expect(isArray([1,2])).to.be.true;\n    });\n})\n```\n\n最后运行script命令即可运行测试\n```shell\nnpm run test\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnapache%2Fmocha-usage-doc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjohnapache%2Fmocha-usage-doc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnapache%2Fmocha-usage-doc/lists"}