【nodejs高可用】单元测试和性能测试

160 阅读7分钟

概述

单元测试和性能测试,对Nodejs的应用很重要,可以保证好系统的质量,避免线上问题,出现问题也可以很快找到,其次就是可能会有某个库,某个方法,方便我们做独立的测试。

从大厂绩效考核的角度上来看,单元测试的质量分,也占了很大的一个比例。

单元测试-AAA模式

AAA代表Arrange、Act、Assert,这三个步骤分别对应于准备测试环境、执行被测功能以及验证结果。以下是关于AAA模式更详细的介绍:

  1. Arrange(准备) :这个阶段主要是设置测试所需的状态或条件。包括但不限于初始化对象、准备数据和配置环境等。这是为了确保测试能够在预定的条件下运行。
  2. Act(行动) :在这一阶段,调用被测试的方法或者函数,传入必要的参数,并获取输出结果。这是测试的核心部分,专注于被测功能的实际执行。
  3. Assert(断言) :最后一步是验证执行的结果是否符合预期。通过检查实际结果与期望结果是否一致来确定测试是否通过。这通常涉及到使用断言语句来进行比较。

可以使用node内置模块 - assert,去做单元测试。

const assert = require('assert');
//测试逻辑。。。

这样其实会有个问题,无法评估错误率,通过率这种,没有报告的形成

这里推荐一个工具Mocha

Mocha

编写测试用例的原则(如何去编写高质量的测试用例):

Fast-测试速度快,跑一次很慢的话,就很难用了。

Independent - 测试不要相互依赖,用例能并行测试。

Repeatable - 可重复,在生产环境,测试环境,本地,都可以这使用,可以验证。

Thorough - 完整的,应该覆盖好全部的测试条件和边界条件,保证可用性。

这里做一个最简单的测试。

新增了一个库

yarn add mocha

例如我需要一个方法把一个数组拆分成数组对象的模式,实现了一个方法arrayToObject

建了一个package.json

{
  "name": "mocha-test",
  "version": "1.0.0",
  "description": "test mocha",
  "main": "index.js",
  "author": "hufeng",
  "license": "MIT",
  "devDependencies": {
    "mocha": "^11.3.0"
  },
  "scripts": {
  "test": "mocha"
}
}

App目录下,index.js:

module.exports = function arrayToObject(arr) {
    const obj = {};
    arr.forEach(([key, value]) => {
        obj[key] = value;
    });
    return obj;
};

Test目录下:

// test/testArrayToObject.js
const assert = require('assert');
const arrayToObject = require('../app/index');
const { request } = require('http');

describe('arrayToObject', function() {
    it('should convert a 2D array to an object', function() {
        const input = [['name', 'Alice'], ['age', 25]];
        const expectedOutput = { name: 'Alice', age: 25 };
        const output = arrayToObject(input);
        console.log(output)
        assert.deepStrictEqual(output, expectedOutput);
    });

    it('should handle empty arrays', function() {
        const input = [];
        const expectedOutput = {};
        const output = arrayToObject(input);
        assert.deepStrictEqual(output, expectedOutput);
    });
});

使用yarn test执行,获取单元测试的结果:

image.png

新增一条单元测试,当输入值为null的时候。

  it('should handle null arrays', function() {
        const input = {};
        const expectedOutput = {};
        const output = arrayToObject(input);
        assert.deepStrictEqual(output, expectedOutput);
    });

这个时候就会报错了,走单测脚本覆盖全所有的情况可以增强程序的健壮性:

image.png

也可以对自己编写的接口做单元测试。

// test/testApp.js
const request = require('supertest');
const app = require('../app');

describe('GET /', function() {
    it('respond with Hello World!', function(done) {
        request(app)
            .get('/')
            .expect('Content-Type', /text/)
            .expect(200)
            .end(function(err, res) {
                if (err) return done(err);
                assert.strictEqual(res.text, 'Hello World!');
                done();
            });
    });
});

describe('GET /user', function() {
    it('respond with json', function(done) {
        request(app)
            .get('/user')
            .expect('Content-Type', /json/)
            .expect(200)
            .end(function(err, res) {
                if (err) return done(err);
                assert.deepStrictEqual(res.body, { name: 'John Doe', age: 25 });
                done();
            });
    });
});

其中,done是异步的,执行一个异步的结果。

性能测试

以下使用Artillery对Nodejs Api做性能测试。

可以通过脚本模拟真实环境的高并发或者网络情况,做极端情况下的测试。

为了观察这种情况一般要配合APM,APM如何搭建,翻我之前的文章去搭配使用就行了。

还可以做稳定性的测试,比如我这个系统跑久一点,会不会有内存泄漏。

极端的情况下,服务是否能够活着,出问题了能不能快速恢复等。

什么场景下需要做性能测试?

(1)新的应用需要上线的时候,预估服务能扛多少QPS,用于后面加机器。

(2)有一定量的访问,比如SSR服务这种,需要做性能测试。

(3)短时间短流量服务,比如抢票,团购这些。

(4)容易被人攻击的服务,携带URL的详情信息这种。

性能测试需要关注的指标?

QPS:每一秒请求数量。

峰值时间QPS/单台机器的QPS = 需要的机器

吞吐量/事务相关(TPS):每秒处理多少事务。

最大响应时间:最慢情况下的响应时间

错误率:错误的请求数占总请求数的占比

磁盘IO/内存:特别是内存泄漏,Nodejs应用需要非常重视。前面有文章发布过,如何查看是否内存泄漏和内存泄漏的原因。

可以参考下面性能测试的方法:

性能测试列表

测试策略环境记录数场景用户数性能指标目的
负载测试一定软件、硬件、网络相同数量级记录运行一种或多种业务不同虚拟用户是否在用户要求范围1. 系统能承载的最大用户数
2. 最大有效用户
3. 不同用户下系统响应时间
4. 服务器资源利用率
压力测试一定软件、硬件、网络相同数量级记录运行一种或多种业务模拟大量用户稳定持续工作让服务器处于极限下长时间连续运行
配置测试不同软硬件配置相同数量级记录运行一种或多种业务一定虚拟用户不同的性能指标值获取不同的性能指标,用于选择最佳的设备及参数配置
容量测试一定软件、硬件、网络构造不同数量级别记录运行一种或多种业务一定虚拟用户不同的性能指标值确认数据库容量
基准测试一定软件、硬件、网络1. 相同数量级记录
2. 不同数量级别记录
运行一种或多种业务根据基准类型(负载、压力、配置、容量、并发)来确认取得不同组合下的性能指标值用于未来:系统调优和系统评测的结果比较,确认调优是否达到效果
并发测试一定软件、硬件、网络相同数量级记录访问同一应用、存储过程、数据记录不同虚拟用户是否在用户要求范围在满足性能要求的情况下,是否有死锁、数据故障、连接泄

使用Artillery做性能测试:

安装脚本:

npm install artillery -g

这里我拉了koa的代码:

git clone https://github.com/koajs/examples.git

在根目录下:

mkdir test

放测试脚本,这里使用了yml文件。

config:
  target: "http://localhost:3000"  # 替换为你的 Koa 服务地址
  phases:
    - duration: 60                 # 测试持续时间(秒)
      arrivalRate: 10             # 每秒发送的请求数(并发量)
      name: "High Load Test"       # 测试阶段名称
  http:
    pipelining: false              # 是否启用 HTTP 管道(通常设为 falsescenarios:
  - name: "Test Koa API"
    flow:
      - get:
          url: "/"         # 替换为你的 Koa 接口路径
          headers:
            Authorization: "Bearer your_token"  # 如果有认证,添加请求头
      - think: 1                   # 模拟用户思考时间(秒)
      # - post:
      #     url: "/api/submit"
      #     json:
      #       key1: "value1"        # POST 请求体数据
      #       key2: "value2"

直接执行artillery run xxx.yml

image.png

可以拿到一些数据,知道我们的压测结果。

这里我加大压力arrivalRate: 1000 # 每秒发送的请求数(并发量)

执行结果:

image.png

这里大概可以知道,我本地的部署,大概每秒能有800多个请求,大多数业务场景下,已经很够打了,有必要的话就加pod去扛就行了。

一般出现CPU这种问题,优先加机器,再去优化代码。

当出现OOM,内存打满那种,应该是代码存在问题,优先优化代码。

其次,arrivalRate支持一些强大的js动态注入参数的能力。

比如这样:

Authorization: "Bearer {{ auth_token }}"  # 使用捕获的 token

如果是分布式的场景:

Artillery也是支持的,可以去看看Artillery的云服务。

或者本地多实例的形式去启动做压测

总之,笔主认为Node应用的性能绝对是很能打的了!在中大厂里,不是别人眼里的“玩具”应用,而是真正能落地,可靠的东西。

具体还是要实践得出结果。