使用Mocha / Chai和async / await验证是否抛出异常

问题描述 投票:14回答:4

我正在努力找出在使用async / await时在Mocha测试中验证承诺被拒绝的最佳方法。

这是一个有效的例子,但我不喜欢should.be.rejectedWith返回一个需要从测试函数返回的promise才能正确评估。使用async / await删除了测试值的这个要求(就像我在下面的wins()的结果一样),我觉得很可能在某些时候会忘记return语句,在这种情况下测试将始终通过。

// Always succeeds
function wins() {
  return new Promise(function(resolve, reject) {
    resolve('Winner');
  });
}

// Always fails with an error
function fails() {
  return new Promise(function(resolve, reject) {
    reject('Contrived Error');
  });
}

it('throws an error', async () => {
  let r = await wins();
  r.should.equal('Winner');

  return fails().should.be.rejectedWith('Contrived Error');
});

感觉应该可以使用async / await将拒绝转换为异常的事实并将其与Chai的should.throw相结合,但我无法确定正确的语法。

理想情况下这可行,但似乎不是:

it('throws an error', async () => {
  let r = await wins();
  r.should.equal('Winner');

  (await fails()).should.throw(Error);
});
node.js async-await mocha chai chai-as-promised
4个回答
18
投票

这种方法的问题是(await fails()).should.throw(Error)没有意义。

await解决了Promise。如果Promise拒绝,它会抛出被拒绝的值。

所以(await fails()).should.throw(Error)永远不会工作:如果fails()拒绝,则会抛出错误,并且.should.throw(Error)永远不会被执行。

您最常用的选择是使用Chai的rejectedWith属性,如您在问题中所示。

这是一个简单的例子。与你在问题中所展示的内容没什么不同;我只是使用async函数为wins()fails()expect而不是should。当然,您可以使用返回Promisechai.should的函数。

const chai = require('chai')
const expect = chai.expect
chai.use(require('chai-as-promised'))

// Always succeeds
async function wins() {
  return 'Winner'
}

// Always fails with an error
async function fails() {
  throw new Error('Contrived Error')
}

it('wins() returns Winner', async () => {
  expect(await wins()).to.equal('Winner')
})

it('fails() throws Error', async () => {
  await expect(fails()).to.be.rejectedWith(Error)
})

如果你想要你的wins()测试更接近你的fails()测试,你可以像这样编写你的wins()测试:

it('wins() returns Winner', async () => {
  await expect(wins()).to.eventually.equal('Winner')
})

在这两个例子中要记住的关键是chai-as-promised返回其函数的承诺,例如rejectedWitheventually.something。因此,您必须在await测试函数的上下文中对它们进行async,否则失败条件仍将通过:

async function wins() {
  return 'Loser'
}

async function fails() {
  return 'Winner'
}

it('wins() returns Winner', async () => {
  expect(wins()).to.eventually.equal('Winner')
})

it('fails() throws Error', async () => {
  expect(fails()).to.be.rejectedWith(Error)
})

如果您使用上面的代码运行测试,您将获得以下内容:

$ npm test

> [email protected] test /home/vsimonian/code/mocha-chai-async
> mocha .



  √ wins() returns Winner
(node:13836) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rej
ection id: 1): AssertionError: expected 'Loser' to equal 'Winner'
(node:13836) [DEP0018] DeprecationWarning: Unhandled promise rejections are dep
recated. In the future, promise rejections that are not handled will terminate
the Node.js process with a non-zero exit code.
  √ fails() throws Error
(node:13836) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rej
ection id: 2): AssertionError: expected promise to be rejected with 'Error' but
 it was fulfilled with 'Winner'

  2 passing (11ms)

正如你所看到的那样,chai断言实际上已经失败了,但它们在Promise的背景下失败了,没有人会用awaited或catched。因此,Mocha没有看到任何失败,并将测试标记为好像已经通过,但Node.js(如上所述,将来会改变的行为)将未处理的拒绝打印到终端。


1
投票

这是我解决问题的方法。

    try {
        // here the function that i expect to will return an errror
        let walletid = await Network.submitTransaction(transaction)
    } catch (error) {
        //  assign error.message to ErrorMessage
        var ErrorMessage = error.message;
        //  catch it and  re throw it in assret.throws fn and pass the error.message as argument and assert it is the same message expected
        assert.throws(() => { throw new Error(ErrorMessage) },'This user already exists');
    }
    // here assert that ErrorMessage is Defined ; if it is not defined it means that no error occurs
    assert.isDefined(ErrorMessage);

0
投票

如果测试你的Promised函数,在测试中必须将代码包装在try / catch中,而expect()必须在catch错误块内

const loserFunc = function(...args) {
  return new Promise((resolve, rejected) => {
    // some code
    return rejected('fail because...');
  });
};

所以,然后在你的测试中

it('it should failt to loserFunc', async function() {
  try {
    await loserFunc(param1, param2, ...);
  } catch(e) {
    expect(e).to.be.a('string');
    expect(e).to.be.equals('fail because...');
  }
});

这是我的方法,不知道更好的方法。


0
投票

我使用这样的自定义函数:

const expectThrowsAsync = async (method, errorMessage) => {
  let error = null
  try {
    await method()
  }
  catch (err) {
    error = err
  }
  expect(error).to.be.an('Error')
  if (errorMessage) {
    expect(error.message).to.equal(errorMessage)
  }
}

然后,对于常规的异步函数,例如:

const login = async (username, password) => {
  if (!username || !password) {
    throw new Error("Invalid username or password")
  }
  //await service.login(username, password)
}

我写这样的测试:

describe('login tests', () => {
  it('should throw validation error when not providing username or passsword', async () => {

    await expectThrowsAsync(() => login())
    await expectThrowsAsync(() => login(), "Invalid username or password")
    await expectThrowsAsync(() => login("username"))
    await expectThrowsAsync(() => login("username"), "Invalid username or password")
    await expectThrowsAsync(() => login(null, "password"))
    await expectThrowsAsync(() => login(null, "password"), "Invalid username or password")

    //login("username","password") will not throw an exception, so expectation will fail
    //await expectThrowsAsync(() => login("username", "password"))
  })
})
© www.soinside.com 2019 - 2024. All rights reserved.