——————————————————————————————————————

单元测试Express/NodeJs

个人理解,

1,如果不是测试http请求的单元测试,用Mocha, Chai等基本够用了,因为可以直接调用测试函数或者方法,传进输入参数值,函数执行完返回输入,对输出断言即可,即可以对此函数独立进行测试。并且可以用istanbul和mocha-lcov-reporter出测试覆盖率报告,也可以出html的形式的报告

另外,如果此函数中需要调用网络上或者其他依赖的api,可能需要mock此api的输入输出,此时可用到Nock包做mock相关的事情。

Sample:

https://github.com/schulzetenberg/nock-test/blob/master/index.js

https://github.com/schulzetenberg/nock-test/blob/master/test/index.spec.js

//被测试的函数

/*jshint esversion: 6 */var Q = require('q');
var Client = require('node-rest-client').Client;exports.getUserFollowers = function(username) {var defer = Q.defer();var client = new Client();
  //此处需要被mockvar request = client.get(`https://api.github.com/users/${username}/followers`, function(data, response) {defer.resolve(data);});request.on('requestTimeout', function(req) {req.abort();defer.reject("Request has expired");});request.on('responseTimeout', function(res) {defer.reject("Response has expired");});request.on('error', function(err) {defer.reject("Request error", err.request.options);});return defer.promise;
};

//单元测试,用nock做mock

var expect = require('chai').expect;
var nock = require('nock');
var getUserFollowers = require('../index').getUserFollowers;describe('GET followers', function() {beforeEach(function() {var followersResponse = [{"login": "octocat","id": 583231,"avatar_url": "https://avatars.githubusercontent.com/u/583231?v=3","gravatar_id": "","url": "https://api.github.com/users/octocat","html_url": "https://github.com/octocat","followers_url": "https://api.github.com/users/octocat/followers","following_url": "https://api.github.com/users/octocat/following{/other_user}","gists_url": "https://api.github.com/users/octocat/gists{/gist_id}","starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}","subscriptions_url": "https://api.github.com/users/octocat/subscriptions","organizations_url": "https://api.github.com/users/octocat/orgs","repos_url": "https://api.github.com/users/octocat/repos","events_url": "https://api.github.com/users/octocat/events{/privacy}","received_events_url": "https://api.github.com/users/octocat/received_events","type": "User","site_admin": false}, {"login": "nanocat","id": 583233,"avatar_url": "https://avatars.githubusercontent.com/u/583233?v=3","gravatar_id": "","url": "https://api.github.com/users/nanocat","html_url": "https://github.com/nanocat","followers_url": "https://api.github.com/users/nanocat/followers","following_url": "https://api.github.com/users/nanocat/following{/other_user}","gists_url": "https://api.github.com/users/nanocat/gists{/gist_id}","starred_url": "https://api.github.com/users/nanocat/starred{/owner}{/repo}","subscriptions_url": "https://api.github.com/users/nanocat/subscriptions","organizations_url": "https://api.github.com/users/nanocat/orgs","repos_url": "https://api.github.com/users/nanocat/repos","events_url": "https://api.github.com/users/nanocat/events{/privacy}","received_events_url": "https://api.github.com/users/nanocat/received_events","type": "User","site_admin": false}];// Mock the TMDB configuration request responsenock('https://api.github.com').get('/users/octocat/followers').reply(200, followersResponse);});it('returns users followers', function() {var username = 'octocat';return getUserFollowers(username).then(function(followers) {// It should return an array objectexpect(Array.isArray(followers)).to.equal(true);// Ensure that at least one follower is in the arrayexpect(followers).to.have.length.above(1);});});
});

本地做测试时,用完nock做完mock后,在请求nock设定的url,只有用http.get(){ res.on('data') ... res.once('end')},现在on data里接收数据,之后在end里取 on data里接收整理过的数据才好用,用request包或者其他的方式都不好用,不知道为什么,可能是兼容性问题

Nock: https://github.com/node-nock/nock

 var req = http.get(`${host_port}/xxx/xxx?a=a1`, function(res) {res.setEncoding('utf8');var body = '';// console.log("http.get res nock nocknock res: " + JSON.stringify(res));res.on('data', function(chunk) {body += chunk;}).once('end', function() {// console.log("http.get res nock body once end : " + body);expect(JSON.parse(body).status).to.equal(httpStatus.OK);scope.done();done();});});

2,如果测试http请求的request/response,就需要对req/res做mock,可以用node-mocks-http,测试过可行。

另一个没有验证的mock框架mock-express:https://www.npmjs.com/package/mock-express

//sample

Workshop - 对Express中间件进行单元测试:http://blog.leapoahead.com/2015/09/09/unittesting-express-middlewares/

tjwudi/unit-testing-express-middlewares-example:https://github.com/tjwudi/unit-testing-express-middlewares-example

//另一个很不错的sample,比较全面

How To Test Your Express Controllers:https://www.terlici.com/2015/09/21/node-express-controller-testing.html

https://github.com/howardabrams/node-mocks-http

——————————————————————————————————————

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

//先了解一下nodejs的单元测试

Node.js 单元测试:我要写测试: 

http://taobaofed.org/blog/2015/12/10/nodejs-unit-tests/

nodejs单元测试ppt:

http://html5ify.com/unittesting/slides/#/1

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

Mocha单元测试简介:

http://unitjs.com/guide/mocha.html

Mocha官网和Github:

https://mochajs.org/

NodeJs测试框架Mocha的简单介绍:http://blog.csdn.net/leoleocs/article/details/50016263

https://github.com/mochajs/mocha

测试框架mochajs详解:

http://www.cnblogs.com/Leo_wl/p/5734889.html

测试框架 Mocha 实例教程《作者: 阮一峰》:

http://www.ruanyifeng.com/blog/2015/12/a-mocha-tutorial-of-examples.html

javascript单元测试框架mochajs详解《了解api》

http://www.cnblogs.com/tzyy/p/5729602.html#_h1_56

Nodejs开源项目里怎么样写测试、CI和代码测试覆盖率:

https://cnodejs.org/topic/558df089ebf9c92d17e73358

wx 是一个不错的微信应用框架,接口和网站做的也不错,和wechat-api是类似的项目

群里有人问哪个好

朴灵说:“不写测试的项目都不是好项目”

确实wx目前还没有测试,对于一个开源项目来说,没有测试和代码覆盖率是不完善的,而且从技术选型来说,大多是不敢选的。

那么Nodejs开源项目里怎么样写测试、CI和代码测试覆盖率呢?

测试

目前主流的就bdd和tdd,自己查一下差异

推荐

  • mocha和tape

另外Jasmine也挺有名,angularjs用它,不过挺麻烦的,还有一个选择是qunit,最初是为jquery测试写的,在nodejs里用还是觉得怪怪的。

如果想简单可以tap,它和tape很像,下文会有详细说明

mocha

mocha是tj写的

https://github.com/mochajs/mocha

var assert = require("assert")
describe('truth', function(){it('should find the truth', function(){assert.equal(1, 1);})
})

断言风格,这里默认是assert,推荐使用chaijs这个模块,它提供3种风格

  • Should
  • Expect
  • Assert

rspec里推荐用expect,其实看个人习惯

比较典型一个mocha例子

var assert = require('chai').assert;
var expect = require('chai').expect;
require('chai').should();describe('Test', function(){before(function() {// runs before all tests in this block})after(function(){// runs after all tests in this block})beforeEach(function(){// runs before each test in this block})afterEach(function(){// runs after each test in this block})describe('#test()', function(){it('should return ok when test finished', function(done){assert.equal('sang_test2', 'sang_test2');var foo = 'bar';expect(foo).to.equal('bar');done()})})
})

说明

  • 理解测试生命周期
  • 理解bdd测试写法

单元测试需要的各个模块说明

  • mocha(Mocha is a feature-rich JavaScript test framework running on node.js and the browser, making asynchronous testing simple and fun.)
  • chai(Chai is a BDD / TDD assertion library for node and the browser that can be delightfully paired with any javascript testing framework.)
  • sinon(Standalone test spies, stubs and mocks for JavaScript.)
  • zombie (页面事件模拟Zombie.js is a lightweight framework for testing client-side JavaScript code in a simulated environment. No browser required.)
  • supertest(接口测试 Super-agent driven library for testing node.js HTTP servers using a fluent API)

更多的看 http://nodeonly.com/2014/11/24/mongoose-test.html

如果你想真正的玩敏捷,从用户故事开始,那么下面这2个库非常必要

  • http://vowsjs.org/
  • https://github.com/cucumber/cucumber-js

啊,黄瓜。。。。

tape:像代码一样跑测试

tape是substack写的测试框架

https://github.com/substack/tape

var test = require('tape').test;
test('equivalence', function(t) {t.equal(1, 1, 'these two numbers are equal');t.end();
});

tape是非常简单的测试框架,核心价值观是”Tests are code”,所以你可以像代码一样跑测试,

比如

node test/test.js

写个脚本就无比简单了。当然如果你想加’test runner’ 库也有现成的。

The Test Anything Protocol

TAP全称是Test Anything Protocol

它是可靠性测试的一种(tried & true)实现

从1987就有了,有很多语言都实现了。

它说白点就是用贼简单的方式来格式化测试结果,比如

TAP version 13
# equivalence
ok 1 these two numbers are equal1..1
# tests 1
# pass  1# ok

比如node里的实现https://github.com/isaacs/node-tap

var tap = require('tap')// you can test stuff just using the top level object.
// no suites or subtests required.tap.equal(1, 1, 'check if numbers still work')
tap.notEqual(1, 2, '1 should not equal 2')// also you can group things into sub-tests.
// Sub-tests will be run in sequential order always,
// so they're great for async things.tap.test('first stuff', function (t) {t.ok(true, 'true is ok')t.similar({a: [1,2,3]}, {a: [1,2,3]})// call t.end() when you're donet.end()
})

一定要区分tap和tape,不要弄混了

科普一下什么是CI

科普一下,CI = Continuous integration 持续集成

Martin Fowler对持续集成是这样定义的:

持续集成是一种软件开发实践,即团队开发成员经常集成他们的工作,通常每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽快地发现集成错误。许多团队发现这个过程可以大大减少集成的问题,让团队能够更快的开发内聚的软件。

它可以

  • 减少风险
  • 减少重复过程
  • 任何时间、任何地点生成可部署的软件
  • 增强项目的可见性
  • 建立团队对开发产品的信心

要素

1.统一的代码库2.自动构建3.自动测试4.每个人每天都要向代码库主干提交代码5.每次代码递交后都会在持续集成服务器上触发一次构建6.保证快速构建7.模拟生产环境的自动测试8.每个人都可以很容易的获取最新可执行的应用程序9.每个人都清楚正在发生的状况10.自动化的部署

也就是说,测试不通过不能部署,只有提交到服务器上,就可以自动跑测试,测试通过后,就可以部署到服务器上了(注意是"staging", 而非"production")。

一般最常的ci软件是jenkins

举个大家熟悉的例子iojs开发中的持续集成就是用的jenkins

https://jenkins-iojs.nodesource.com/

jenkins是自建环境下用的比较多,如果是开源项目,推荐travis-ci

https://travis-ci.org/

对开源项目做持续集成是免费的(非开源的好贵),所以在github集成的基本是最多的。

对nodejs支持的也非常好。

举2个例子

  • express https://travis-ci.org/strongloop/express
  • koa https://travis-ci.org/koajs/koa

测试报告

近年随着tdd/bdd,开源项目,和敏捷开发的火热,程序员们不再满足说,我贡献了一个开源项目

要有高要求,我要加测试

要有更高要求,我要把每一个函数都测试到,让别人相信我的代码没有任何问题

上一小节讲的ci,实际上解决了反复测试的自动化问题。但是如何看我的程序里的每一个函数都测试了呢?

答案是测试覆盖率

在nodejs里,推荐istanbul

Istanbul - 官方介绍 a JS code coverage tool written in JS

它可以通过3种途径生成覆盖报告

  • cli
  • 代码
  • gulp插件

安装

$ npm install -g istanbul

执行

$ istanbul cover my-test-script.js -- my test args

它会生成./coverage目录,这里面就是测试报告

比如我的项目里

./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly#MongooseDao()✓ should return ok when record create✓ should return ok when record delete fixture-user✓ should return ok when record deleteById✓ should return ok when record removeById✓ should return ok when record getById✓ should return ok when record getAll✓ should return ok when record all✓ should return ok when record query8 passing (50ms)=============================================================================
Writing coverage object [/Users/sang/workspace/moa/mongoosedao/coverage/coverage.json]
Writing coverage reports at [/Users/sang/workspace/moa/mongoosedao/coverage]
============================================================================================================ Coverage summary ===============================
Statements   : 47.27% ( 26/55 )
Branches     : 8.33% ( 1/12 )
Functions    : 60% ( 9/15 )
Lines        : 47.27% ( 26/55 )
================================================================================

默认,它会生成coverage.json和Icov.info,如果你想生成html也可以的。

比如说,上面的结果47.27%是我测试覆盖的占比,即55个函数,我的测试里只覆盖了26个。

那么我需要有地方能够展示出来啊

实践

我们以mongoosedao项目为例,介绍一下如何集成测试,ci和测试覆盖率

最终效果如图

npm run

package.json里定义自定义执行脚本

"scripts": {"start": "npm publish .","test": "./node_modules/.bin/gulp","mocha": "./node_modules/.bin/mocha -u bdd","cov":"./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage"
},

除了start和test外,都是自定义任务,其他都要加run命令

npm run mocha
npm run cov

更多见npm-run-test教程

gulp watch

var gulp = require('gulp');
var watch = require('gulp-watch');var path = 'test/**/*.js';gulp.task('watch', function() {gulp.watch(['test/**/*.js', 'lib/*.js'], ['mocha']);
});var mocha = require('gulp-mocha');gulp.task('mocha', function () {return gulp.src(path , {read: false})// gulp-mocha needs filepaths so you can't have any plugins before it .pipe(mocha({reporter: 'spec'}));
});gulp.task('default',['mocha', 'watch']);

这样就可以执行gulp的时候,当文件变动,会自动触发mocha测试,简化每次都输入npm test这样的操作。

当然你可以玩更多的gulp,如果不熟悉,参考

  • 介绍gulp的一张不错的图
  • gulp实践

创建.travis.yml

项目根目录下,和package.json平级

language: node_js
repo_token: COVERALLS.IO_TOKEN
services: mongodb
node_js:- "0.12"- "0.11"- "0.10"
script: npm run mocha
after_script:npm run cov

说明

  • 如果依赖mongo等数据库,一定要写services
  • 把测试覆盖率放到执行测试之后,避免报402错误

在travis-ci.org上,github授权,添加repo都比较简单

添加之后,就可以看到,比如

https://travis-ci.org/moajs/mongoosedao

travis-ci实际上根据github的代码变动进行自动持续构建,但是有的时候它不一定更新,或者说,你需要手动选一下:

点击# 10 passed,这样就可以强制它手动集成了。

其他都很简单,注意替换COVERALLS.IO_TOKEN即可。

创建 .coveralls.yml

https://coveralls.io/是一个代码测试覆盖率的网站,

nodejs下面的代码测试覆盖率,原理是通过istanbul生成测试数据,上传到coveralls网站上,然后以badge的形式展示出来

比如

具体实践和travis-ci类似,用github账号登陆,然后添加repo,然后在项目根目录下,和package.json平级,增加.coveralls.yml

service_name: travis-pro
repo_token: 99UNur6O7ksBqiwgg1NG1sSFhmu78A0t7

在上,第一次添加repo,显示的是“SET UP COVERALLS”,里面有token,需要放到.coveralls.yml里,

如果成功提交了,就可以看到数据了

在readme.md里增加badge

[![Build Status](https://travis-ci.org/moajs/mongoosedao.png?branch=master)](https://travis-ci.org/moajs/mongoosedao)
[![Coverage Status](https://coveralls.io/repos/moajs/mongoosedao/badge.png)](https://coveralls.io/r/moajs/mongoosedao)

它就会显示如下

另外一种用Makefile的玩法实践

举例:https://github.com/node-webot/wechat-api/blob/master/Makefile

TESTS = test/*.js
REPORTER = spec
TIMEOUT = 20000
ISTANBUL = ./node_modules/.bin/istanbul
MOCHA = ./node_modules/mocha/bin/_mocha
COVERALLS = ./node_modules/coveralls/bin/coveralls.jstest:@NODE_ENV=test $(MOCHA) -R $(REPORTER) -t $(TIMEOUT) \$(MOCHA_OPTS) \$(TESTS)test-cov:@$(ISTANBUL) cover --report html $(MOCHA) -- -t $(TIMEOUT) -R spec $(TESTS)test-coveralls:@$(ISTANBUL) cover --report lcovonly $(MOCHA) -- -t $(TIMEOUT) -R spec $(TESTS)@echo TRAVIS_JOB_ID $(TRAVIS_JOB_ID)@cat ./coverage/lcov.info | $(COVERALLS) && rm -rf ./coveragetest-all: test test-coveralls.PHONY: test

我个人更喜欢npm+gulp的写法,总是有一种make是c里古老的东东。。。

总结

本文讲了

  • nodejs里常用框架

    • mocha
    • tape
    • tap
    • 前沿技术:cucumber和vowsjs
  • 科普一下CI
  • 测试报告
    • istanbul
  • 实践
    • gulp + npm run
    • mocha
    • travis-ci
    • coveralls
  • 介绍了基于makefile的另一种玩法

全文完

Node.js 单元测试:我要写测试 - Mocha - Nodejs开源项目里怎么样写测试、CI和代码测试覆盖率相关推荐

  1. node mysql商城开发_NideShop:基于Node.js+MySQL开发的微信小程序商城开源啦

    NideShop:基于Node.js+MySQL开发的微信小程序商城开源啦 发布时间:2020-04-14 04:23:37 来源:51CTO 阅读:2894 作者:ch10mmt 高仿网易严选的微信 ...

  2. webstorm配置环境变量_sulky环境配置,node.js安装以及如何运行webstorm的web app项目...

    该文章用来记笔记用的,主要记录如何在webstorm上运行一个app项目,希望对于需要的人有帮助. 首先,要运行web app项目,必须先装好相应的环境以及配置. 其中node.js和其他需要的配置在 ...

  3. 人工智能入门第一课:手写字体识别及可视化项目(手写画板)(mnist)

    人工智能入门第一课:手写字体识别及可视化项目(手写画板)(mnist),使用技术(Django+js+tensorflow+html+bootstrap+inspinia框架) 直接上图,项目效果 1 ...

  4. Node.js -- Stream 使用小例 ( 流运用 :读取、写入、写出、拷贝)

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. Stream 是一个抽象接口,Node 中有很多对象实现了这个接口.例如,对http 服务器发起请求 ...

  5. Node.js篇01-安装与配置,基本使用,服务器,项目搭建,调试

    1.安装与配置 官网下载, 并配置环境变量 查看安装成功/版本号: DOS命令 node -v 运行JS文件: node 文件名.js 2.基本使用 (1)CommonJS模块化 Node.js遵循了 ...

  6. node和php处理高并发,node.js“多线程”如何处理高并发任务?,nodejs java 高并发

    node.js"多线程"如何处理高并发任务?node . js"多线程"是如何处理高度并发的任务的?,下面的文章介绍了使用nodejs"多线程&quo ...

  7. 安装node-v4.5.0-x86.msi时提示node.js setup wizard ended prematurely Win7安装nodejs失败解决方法

    文章总共3个方法,后两个方法可安装成功 方法1:在网上搜索node.js setup wizard ended prematurely时找到一个参考解决方法 参考地址:http://blog.csdn ...

  8. 一名测试工程师参与开源项目的经历

    一.初识开源 其实很早就想参与开源项目,但是因为个人编码能力.没有时间等等原因,迟迟没有开始.决定开始参与开源是在从坐班工作换到远程工作之后,有了比较多的时间,一次在逛电鸭论坛时,看到了<开源面 ...

  9. android 测试工具,Android开源项目第四篇:开发及测试工具篇

    本文为那些不错的Android开源项目第四篇--开发工具篇,**主要介绍Android开发工具和测试工具相关的开源项目**. Android开源项目系列汇总已完成,包括: 1.Buck faceboo ...

最新文章

  1. 令人拍案叫绝的Wasserstein GAN
  2. 利用xsl和xml转html没有内容_P系列2代码页转pdf后篇
  3. 异常的分级分类与处理策略
  4. Spring Boot&Angular 5&Spring Data&Rest示例(CRUD)
  5. 带电检测必要性_接地电阻检测分析的必要性
  6. 记录——《C Primer Plus (第五版)》第七章编程练习第四题
  7. Oracle 执行长SQL
  8. 你真的理解机器学习中偏差 - 方差之间的权衡吗?
  9. vim mark bookmarks使用
  10. 原创 | 职场风云 (三)面对压力
  11. 【Hoxton.SR1版本】Spring Cloud Gateway之Predicate详解
  12. vs编译时出现大量ws2def.h的错误的解决方法
  13. python 折线图平滑_使用matplotlib的平滑折线图
  14. 纯干货,PSI 原理解析与应用
  15. php 读doc_PHP读取DOC 文件
  16. 如何避免自由软件项目中的需求变更
  17. Excel图表:随心所欲模拟坐标轴
  18. x264源代码简单分析 编码器主干部分-1
  19. 钉钉调用新版待办任务
  20. IntellIdea+SpingMVC简单项目

热门文章

  1. Kubernetes 为何称为 K8s?
  2. 简单解释:STM32参考手册的地址偏移和复位值
  3. 图像处理 图像相似算法aHash、dHash、pHash解析与对比
  4. docker基础篇--有它就够了
  5. Win10 DirectX 8.1 安装与启用
  6. c语言哑铃,使用一副哑铃,做好8个动作,就能练遍全身肌肉
  7. 渠道商用假流量冒充真实用户
  8. 从“Real如我”来看当前社交APP开发-深圳积木创意科技
  9. win101909要不要更新_近年最稳的Win10更新?Win10 1909值得升级吗
  10. 监控系统选型,一文轻松解决