Chai中to.throw和to.be.rejectWith的区别

在使用 Chai 断言库进行测试时,expect(...).to.throw()expect(...).to.be.rejectedWith() 是两种不同的断言方式,主要用于验证不同类型的错误抛出:

1. expect(...).to.throw()

  • 用途:验证同步函数调用是否抛出错误。
  • 适用场景:普通函数、构造函数、同步方法等。
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 同步函数抛出错误
    function throwError() {
    throw new Error('Something went wrong');
    }

    // 断言同步函数调用会抛出错误
    expect(throwError).to.throw('Something went wrong');

    // 也可以用于方法调用(用箭头函数包裹)
    expect(() => game.addPlayer(player)).to.throw('玩家已存在');

2. expect(...).to.be.rejectedWith()

  • 用途:验证异步操作(Promise)是否被拒绝(rejected)并携带特定错误信息。
  • 适用场景:异步函数、返回 Promise 的方法等。
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 异步函数返回 rejected Promise
    async function asyncThrowError() {
    throw new Error('Async error');
    }

    // 断言异步操作会被拒绝并携带特定错误
    await expect(asyncThrowError()).to.be.rejectedWith('Async error');

    // 或对返回 Promise 的方法进行断言
    await expect(game.joinAsync(player)).to.be.rejectedWith('游戏已满');

关键区别总结

特性 expect(...).to.throw() expect(...).to.be.rejectedWith()
验证对象 同步函数调用 异步操作(Promise)
语法要求 需要传入函数引用(或用箭头函数包裹) 直接传入 Promise 或使用 await
错误类型 捕获同步抛出的错误(throw) 捕获 Promise 被拒绝(reject)的情况
异步测试支持 不支持(无法直接验证 Promise) 必须使用(支持 async/await
示例 expect(() => fn()).to.throw() await expect(promise).to.be.rejected()

常见误区

  • 误用 to.throw() 测试异步代码

    1
    2
    3
    4
    5
    // 错误:无法捕获 Promise 的拒绝
    expect(game.joinAsync(player)).to.throw();

    // 正确:使用 rejectedWith
    await expect(game.joinAsync(player)).to.be.rejectedWith('错误信息');
  • 忘记 await 关键字

    1
    2
    3
    4
    5
    // 错误:断言会立即执行,不会等待 Promise 完成
    expect(game.joinAsync(player)).to.be.rejectedWith('错误信息');

    // 正确:等待 Promise 完成
    await expect(game.joinAsync(player)).to.be.rejectedWith('错误信息');

何时使用哪个?

  • 同步代码(普通函数、方法):使用 to.throw()
  • 异步代码(返回 Promise 的函数/方法):使用 to.be.rejectedWith(),并确保测试函数为 async 且使用 await

如果你的 addPlayer 方法是同步的(如最初代码所示),则应使用 to.throw();如果该方法改为异步(返回 Promise),则需使用 to.be.rejectedWith()