NodJs 单元测试写法以及将测试报告推送到sonarqube

nodejs 后端单元测试写法示例

个人见解: 对于一些复杂的业务逻辑还是应该写单元测试,以确保有将所有的情况考虑清楚

1. 安装测试依赖

npm i nyc mocha sinon chai -s
  • nyc 用于生成各种格式测试报告 官网
  • mocha 整合测试用例,测试的主要框架 官网
  • sinon 用于 mock 实例对象的方法,返回指定的数据 官网
  • chai 测试断言库,对测试结果进行判断 官网

2. 代码示例

// A.js
class A {
  add(a, b) {
    return a + b;
  }
}

module.exports = A;

// A.test.js
const sinon = require('sinon');
const expect = require('chai').expect;
const A = require('../A');

// 为每个测试用例创建独立的 mock 环境,为了测试用例之间的 stub 不相互干扰
let sandbox;
beforeEach(() => {
  sandbox = sinon.createSandbox();
})

afterEach(() => {
  sandbox.restore();
})

describe('unit test', () => {

  it ('a + b', () => {
    const a = new A();
    const result = a.add(1, 2);
    expect(result).to.equal(3);
  })

  // mock 对象的方法,返回指定的值
  it ('mock add()', () => {
    const a= new A();
    sandbox.stub(a, 'add').returns(4);
    const result = a.add(1, 2);
    expect(result).to.equal(4);
  })
})

3. 测试的执行

node_modules\.bin\mocha  A.test.js  
  • mocha 没有全局安装,所以这样执行
  • 执行参考可以指定测试文件,不指定默认会执行 test 目录下的测试文件
    在这里插入图片描述

4. 实际应用中生成测试报告

  • package.json 中增加 "test": "nyc --reporter=lcov mocha --exit" 其中 lcov 格式的报告可以被 sonarqube 识别,从而利用 sonarscaner 将测试结果推送到 sonarqube 服务器
  • 运行 npm run test 即可在 coverage 目录看到测试结果
    在这里插入图片描述

5. windows 下安装 sonarqube 服务器

参考: https://blog.csdn.net/JackSparrowlj/article/details/90512652

  • 下载 sonarqube: http://www.sonarqube.org/downloads/
  • 启动 sonarqube: windows-x86-64/StartSonar.bat
  • 访问并修改密码:127.0.0.1:9000 默认密码: admin/admin 修改为 admin/root
  • 创建登录 token -> 右上角 My Account -> Security -> Generate Tokens

6. windows 下安装 sonarscanner

参考: https://docs.sonarqube.org/latest/analysis/scan/sonarscanner/

  • 解压后在 path 上加入 sonar-scanner 执行路径: bin/sonar-scanner.bat

7. 利用 sonarscanner 命令将测试报告推送到 sonarqube 服务器

sonar-scanner -D"sonar.projectKey=ut" -D"sonar.sources=src/common" -D"sonar.host.url=http://10.50.160.15:9000" -D"sonar.login=663dcd204cdbeeb09814ecc71316379077af5246" -D"sonar.javascript.lcov.reportPaths=./coverage/lcov.info"
  • sonar-scanner 全局执行需要将 下载后的 sonarscanner 中 sonar-scanner.bat 加入到环境变量 path 上
  • sonar.sources 指定扫描的文件
  • sonar.host.url sonarqube 服务器
  • sonar.login sonarqube 创建的 token
  • sonar.javascript.lcov.reportPaths 测试报告的文件路径
  • sonar.zaproxy.reportPath 可以指定 zaproxy 安全扫描的报告路径
  • sonar.exclusions 扫描时排除的文件
    在这里插入图片描述
sonar 其他配置方法
  • 创建 sonar-project.properties 文件
  • 配置 sonar-scanner 执行参数
sonar.projectKey=test
sonar.sources=src/app
sonar.host.url=http://127.0.0.1:9000
sonar.login=7cc73ced504aaeac63a73a17cde0d1773ca3734a
sonar.javascript.lcov.reportPaths=./coverage/ngv/lcov.info
  • 配置后只需执行 sonar-scanner 命令即可,默认会去 sonar-project.properties 获取参数
Angular 生成测试报告并推送到 sonarqube
  • 修改 karma.conf.js 生成 lcvo 格式报告
coverageReporter: {
      dir: require('path').join(__dirname, './coverage/ngv'),
      subdir: '.',
      reporters: [
        { type: 'lcov' }, // lcov 会生成 html,lcov 格式; lcovonly 只生成 lcov, html 默认格式
        { type: 'text-summary' }
      ]
    },
  • 执行测试生成报告 ng test --code-coverage=true
  • 推送测试结果到 sonarqube, 执行 sonar-scanner 命令即可

8. 带有 callback 的方法 mock 写法

cconst sinon = require('sinon');
// 因为 nodejs 同一个模块只会加载一次然后缓存起来,所以这里 mock fs 模块方法,那么其他模块再次使用该模块的时候使用的是缓存里面的已经mock了的
const fs = require('fs');

// 1. 有 callback 参数的方法调用,返回指定的值
// callsFake 后面的 function 参数为 readFile 的参数
// 调用 cb 则是调用 fs.readFile 时写的 callback 方法
sinon.stub(fs, 'readFile').callsFake((fileName, cb) => {
  // 此处相当于手动调用 fs.readFile('file', (err, buf) => {}) 中的 callback 方法 => (err, buf) => {}
  // 且可以自行调用时的传参可指定, fs.readFile 其结果实际上是通过 callback 带出来的
  cb(null, Buffer.from('readFile')); // cb 的参数写法由 fs.readFile callback决定
});
fs.readFile('./test/A.test.js', (err, buf) => {
  console.log(buf.toString()); // 结果 => readFile
})

// 2. 无 callback 方法 mock 返回指定的值
sinon.stub(fs, 'readFileSync').returns('test');
const data = fs.readFileSync('./test/A.test.js');
console.log(data.toString()); // 结果 => test

9. new Class 方法如何 Mock

首先要了解 js 中 class 是基于原型链实现的,class 中创建的方法实际上都在 Class.prototype 上,所有只需要 mock Class.prototype 上对应的方法则可以 mock new Class 创建实例所对应的所有方法

const sinon = require('sinon');

class Student {
  work() {
    return 'hard work'
  }
}
// mock Student 所有实例的 work 方法
sinon.stub(Student.prototype, 'work').returns('test');
// 创建实例
const s = new Student();
console.log(s.work()); // 结果 => test  (mock 成功).

10. 测试一个模块,根据 case 不同进行分组测试写法

describe('测试一个模块', function() {
  describe('case 1', function() {
    it('1.1', function(done) {
      done();
    })
    it('1.2', function(done) {
      done();
    })
  })

  describe('case 2', function() {
    it('2.1', function(done) {
      done();
    })
    it('2.2', function(done) {
      done();
    })
  })
})

测试结果:
在这里插入图片描述

11. 模块方法的 mock, 之后引用了该模块方法的地方都会被 mock

原理: nodejs 相同模块只会加载一次并缓存起来
参考: https://juejin.cn/post/6899752077807845383

// A.js
function add(a, b) {
	return a + b;
}
module.exports = {add}
// B.js
const A = require('./A');
function add() {
	return A.add(1, 1);
}
module.exports = {add}
// test.js
const sinon = require('sinon');
const A = require('./A');
const B = require('./B');
// 是对 A 模块 add 方法的 mock, 此时 B.add 中调用的 A.add 会被 mock 从而返回 3
// 要求 module.exports 的不是 Class, Class 需要通过 sionon.stub(Class.prototype, method) 方法来 mock
sinon.stub(A, 'add').returns(3);
console.log(B.add()); // 结果 => 3 而不是 2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值