NodeJs如何做API接口单元测试? --【elpis全栈项目】

NodeJs API接口单元测试

api单元测试需要用到的

  • assert:断言库 (还要一些断言库比如:Chai)
  • supertest: 模拟http请求

简单的例子:

javascript 复制代码
const express = require('express');
const supertest = require('supertest');
const assert = require('assert');

// 创建一个 Express 应用
const app = express();
app.get('/api/user', (req, res) => {
  res.status(200).json({ name: 'John Doe' });
});

// 测试用例
describe('GET /api/user', () => {
  it('应该返回用户信息', async () => {
  	const request = supertest(app)
    const res = await request()
      .get('/api/user') // 发送 GET 请求
      .set('Accept', 'application/json') // 设置请求头
      .expect('Content-Type', /json/) // 验证响应头
      .expect(200); // 验证状态码

    // 验证响应体
    assert.strictEqual(res.body.name, 'John Doe');
  });
});

一、 断言


  1. Node.js 有内置的断言模块assert,用于在代码中验证某些条件是否为真。如果条件不满足,assert 会抛出一个 AssertionError,导致测试失败。

    简单介绍一些NodeJs内置的断言assert的一些属性方法:

    1. assert.ok(value[, message]) 可以直接简写为assert() 验证value 是否为真
      • assert.ok(1); // 通过
      • assert.ok(0, '值不能为 0'); // 抛出 AssertionError,错误信息为 "值不能为 0"
    2. assert.strictEqual(actual, expected[, message]) 验证是否 严格相等(===)
      • assert.strictEqual(1, 1); // 通过
    3. assert.notStrictEqual(actual, expected[, message]) 验证是否 不严格相等(!==)
    4. assert.deepStrictEqual(actual, expected[, message]) 验证是否 深度严格相等 (适用于对象或数组)
      • assert.deepStrictEqual({ a: 1 }, { a: '1' }); // 抛出 AssertionError,因为类型不同
    5. assert.notDeepStrictEqual(actual, expected[, message]) 验证是否 深度不严格相等(上面的例子不会抛错)
    6. assert.equal(actual, expected[, message]) 验证是否 相等(==,非严格相等)
    7. assert.notEqual(actual, expected[, message]) 验证是否不相等(!=,非严格相等)
    8. assert.throws(block[, error][, message]) 验证 block 函数是否会 抛出错误
    javascript 复制代码
     assert.throws(
      () => {
        throw new Error('错误信息');
      },
      Error, // 验证错误类型
      '未抛出预期错误' // 自定义错误信息
    );
    1. assert.doesNotThrow(block[, error][, message]) 验证 block 函数是否 不会抛出错误
    2. assert.fail([message]) 强制抛出 一个 AssertionError,标记测试失败assert.fail('测试失败')
    3. 总之: assert 是 Node.js 内置的断言模块,适合简单的测试场景。如果需要更丰富的功能和更友好的语法,可以考虑使用 chai 等第三方断言库。
  2. 还有一些第三方库,比如chai,它支持多种风格的断言。Chai是一个可以在node和浏览器环境运行的 BDD/TDD 断言库,可以和任何JavaScript测试框架结合。

按使用风格:

  • assert 风格:类似于 Node.js 内置的 assert,但功能更强大。
  • expect 风格:链式语法,可读性更高。
  • should 风格:基于原型链的语法,适合 BDD(行为驱动开发)。
javascript 复制代码
// assert 风格
assert(res.body.success === true)

// expect 风格
expect(1 + 1).to.equal(2);
expect({ a: 1 }).to.deep.equal({ a: 1 });

// should 风格
chai.should();
(1 + 1).should.equal(2);

按测试风格:

  • BDD(Behavior Driven Development行为驱动开发):是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、QA和非技术人员或商业参与者之间的协作(做正确的事)

  • TDD(Test-Driven Development测试驱动开发): 测试先于编写代码的思想用于指导软件开发(正确的做事)

    expect和should是BDD风格的。两者使用相同的语言链

    • expect使用构造函数来创建断言对象实例
    • should使用Object.prototype提供一个getter方法来实现,不兼容IE

    assert属于TDD

    • 除语法糖外,assert和node.js的非常相似,assert是三种断言风格唯一不支持链式调用的

chai详细介绍:学习Chai断言库

二、 supertest, 用于测试 HTTP 服务的库


supertest 是一个用于测试 HTTP 服务的 Node.js 库,特别适合测试 Express 或其他基于 Node.js 的 Web 服务器。它提供了简洁的 API 来构造和发送 HTTP 请求,并验证响应结果。supertest 通常与测试框架(如 Mocha、Jest 等)结合使用,用于编写端到端(E2E)测试或集成测试。

javascript 复制代码
...
// 测试用例
describe('GET /api/user', () => {
  it('应该返回用户信息', async () => {
  	const request = supertest(app)
    const res = await request()
      .get('/api/user') // 发送 GET 请求
      .set('Accept', 'application/json') // 设置请求头
      .expect('Content-Type', /json/) // expect属于supertest内置断言,验证响应头
      .expect(200); // supertest内置断言,验证状态码
  });
});
...

下面列举一些supertest属性方法:

  1. 构造请求
    • .get(url):发送 GET 请求。
    • .post(url):发送 POST 请求。
    • .put(url):发送 PUT 请求。
    • .delete(url):发送 DELETE 请求。
    • .patch(url):发送 PATCH 请求。
    • .head(url):发送 HEAD 请求。
  2. 设置请求头 , .set(field, value) 例如:
    • .set('Authorization', 'Bearer token') // 设置 Authorization 头
    • .set('Accept', 'application/json'); // 设置 Accept 头
  3. 发送请求体 ,使用.send(data)方法可以发送请求体,适用于 POST、PUT 等请求。
    • .send({ name: 'John', age: 30 })
  4. 设置查询参数 , 使用.query(params)方法可以设置查询参数。
    • .query({ page: 1, limit: 10 }); // 设置查询参数 ?page=1&limit=10
  5. 设置请求类型 , 使用.type(type)方法可以设置请求的 Content-Type
    • .type('json') // 设置 Content-Type 为 application/json
    • .type('form') // 设置 Content-Type 为 application/x-www-form-urlencoded
    • .type('text') // 设置 Content-Type 为 text/plain
  6. 文件上传 , 使用 .attach(field, file) 方法可以上传文件。
    • .attach('file', 'path/to/file.txt'); // 上传文件
  7. 验证响应 , 内置了.expect(...)方法,与第三方断言库库chai的expect作用类似,只是supertest的内置方法更专注于 HTTP 响应的验证, 而 chai 的 expect 是一个通用的断言库,适用于更广泛的场景。
    • .expect(status):验证状态码。
    • .expect(header, value):验证响应头。
    • .expect(body):验证响应体。
    • .expect(function(res) { ... }):自定义验证逻辑。
  8. 处理响应 ,使用 .end(callback) 方法可以处理响应结果。
    • .get('/api/data').end((err, res) => { if (err) throw err; ...})
  9. 超时设置 , 使用.timeout(ms)方法可以设置请求的超时时间。
  10. 重定向 , 使用 .redirects(n) 方法可以设置最大重定向次数。
    • .redirects(2) // 最多允许 2 次重定向
  11. Cookie , 使用 .set('Cookie', cookie) 方法可以设置请求的 Cookie。
  12. 自定义 Agent , 使用 .agent() 方法可以创建一个自定义的 superagent 实例,用于保持会话
javascript 复制代码
const agent = request.agent(app);

agent
  .post('/api/login')
  .send({ username: 'john', password: '123456' })
  .end((err, res) => {
    if (err) throw err;
    agent.get('/api/data').end((err, res) => {
      if (err) throw err;
      console.log(res.body);
    });
  });
  1. 响应对象属性 , 在 .end().expect() 的回调函数中,可以访问响应对象的属性。
javascript 复制代码
 .end((err, res) => {
    if (err) throw err;
    console.log(res.status); // 状态码
    console.log(res.headers['content-type']); // 响应头
    console.log(res.body); // 响应体
    console.log( res.text); // 原始响应文本。
  });

三、完整配置


我在项目中的配置:(也不全,简单参考一下)

javascript 复制代码
const assert = require('assert');
const supertest = require('supertest');
const md5 = require('md5');
const elpisCore = require('../../elpis-core')

const signKey = '620b048b-8ac3-431b-845d-bcaf63ecc738'
const st = Date.now();

describe('测试 project 相关接口', function () {
  this.timeout(60000);

  let request;

  it('启动服务', async () => {
    const app = await elpisCore.start();
    request = supertest(app.listen());
  });

  it('GET /api/project/model_list', async () => {
    let tmpRequest = request.get('/api/project/model_list');
    tmpRequest = tmpRequest.set('s_t', st);
    tmpRequest = tmpRequest.set('s_sign', md5(`${signKey}_${st}`))
    const res = await tmpRequest;
    assert(res.body.success === true)

    const resData = res.body.data;
    assert(resData.length > 0);

    for (let i = 0; i < resData.length; i++) {
      const item = resData[i];
      assert(item.model);
      assert(item.model.key);
      assert(item.model.name);
      assert(item.project);
      for (const projKey in item.project) {
        assert(item.project[projKey].key);
        assert(item.project[projKey].name);
      }
    }
  });

})
相关推荐
jump_jump9 小时前
超长定时器 long-timeout
前端·javascript·node.js
小熊出擊15 小时前
【pytest】finalizer 执行顺序:FILO 原则
python·测试工具·单元测试·pytest
xjf771118 小时前
Nx项目中使用Vitest对原生JS组件进行单元测试
javascript·单元测试·前端框架·nx·vitest·前端测试
牧码岛21 小时前
服务端之NestJS接口响应message编写规范详解、写给前后端都舒服的接口、API提示信息标准化
服务器·后端·node.js·nestjs
嚴寒1 天前
Node 版本管理还在手动重装全局包?这个方案让你效率翻倍
node.js
杨小熊的笔记2 天前
final字段单元测试
单元测试·log4j
你的人类朋友2 天前
【Node】单线程的Node.js为什么可以实现多线程?
前端·后端·node.js
HoJunjie2 天前
macOS sequoia 15.7.1 源码安装node14,并加入nvm管理教程
macos·node.js
做运维的阿瑞3 天前
Windows 环境下安装 Node.js 和 Vue.js 框架完全指南
前端·javascript·vue.js·windows·node.js