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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/45466040/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 18:23:55  来源:igfitidea点击:

Verify that an exception is thrown using Mocha / Chai and async/await

node.jsasync-awaitmochachaichai-as-promised

提问by plexer

I'm struggling to work out the best way to verify that a promise is rejected in a Mocha test while using async/await.

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

Here's an example that works, but I dislike that should.be.rejectedWithreturns a promise that needs to be returned from the test function to be evaluated properly. Using async/await removes this requirement for testing values (as I do for the result of wins()below), and I feel that it is likely that I will forget the return statement at some point, in which case the test will always pass.

这是一个有效的示例,但我不喜欢它should.be.rejectedWith返回一个需要从测试函数返回才能正确评估的承诺。使用 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');
});

It feels like it should be possible to use the fact that async/await translates rejections to exceptions and combine that with Chai's should.throw, but I haven't been able to determine the correct syntax.

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

Ideally this would work, but does not seem to:

理想情况下,这会起作用,但似乎不起作用:

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

  (await fails()).should.throw(Error);
});

回答by Adaline Simonian

The problem with this approach is that (await fails()).should.throw(Error)doesn't make sense.

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

awaitresolves a Promise. If the Promiserejects, it throws the rejected value.

await解决一个Promise. 如果Promise拒绝,则抛出被拒绝的值。

So (await fails()).should.throw(Error)can never work: if fails()rejects, an error is thrown, and .should.throw(Error)is never executed.

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

The most idiomatic option you have is to use Chai's rejectedWithproperty, as you have shown in your question.

rejectedWith正如您在问题中所示,您拥有的最惯用的选择是使用柴的财产。

Here's a quick example. Not much is different from what you've demonstrated in your question; I'm just using asyncfunctions for wins()and fails()and expectinstead of should. Of course, you can use functions that return a Promiseand chai.shouldjust fine.

这是一个快速示例。与您在问题中展示的内容没有太大区别;我只是使用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)
})

If you like want your wins()test to resemble your fails()test more closely, you can write your wins()test like so:

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

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

The key thing to remember in either of these examples is that chai-as-promisedreturns promises for its functions such as rejectedWithand eventually.something. Therefore you must awaitthem in the context of an asynctest function, or else failing conditions will still pass:

在这些示例中的任何一个中要记住的关键是chai-as-promised返回其函数的承诺,例如rejectedWithand eventually.something。因此,您必须awaitasync测试函数的上下文中使用它们,否则失败的条件仍然会通过:

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)
})

If you ran the tests with the code above, you'd get the following:

如果你用上面的代码运行测试,你会得到以下结果:

$ 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)

As you can see, the chai assertions actually failed, but they were failed in the context of a Promise that no one ever awaited or catched. So Mocha sees no failure and marks the tests as though they passed, but Node.js (in behaviour that will change in the future as noted above) prints the unhandled rejections to the terminal.

如您所见,chai 断言实际上失败了,但是它们在没有人await编辑或catch编辑过的 Promise 的上下文中失败了。所以 Mocha 认为没有失败并将测试标记为通过,但 Node.js(其行为将在未来改变,如上所述)将未处理的拒绝打印到终端。

回答by kord

I use a custom function like this:

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

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)
  }
}

and then, for a regular async function like:

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

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

I write the tests like this:

我写这样的测试:

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"))
  })
})

回答by saketh

You can use async/awaitand shouldfor simple verification

您可以使用async/awaitshould进行简单的验证

it('should not throw an error', async () => {
  try {
    let r = await wins();
    r.should.equal('Winner');
  } catch (error) {
    error.should.be.null(); //should.not.exist(error) can also be used
  }
});

it('throws an error', async () => {
  try {
    await fails();
  } catch (error) {
    error.should.be.Error();
    error.should.have.value("message", "Contrived Error");
  }
});

回答by Dani GTA

This example only works with Node!

此示例仅适用于 Node!

When you use Mocha on Node.js you can use doesNotReject()or rejects()both require a function that returns a promise.

当您在 Node.js 上使用 Mocha 时,您可以使用doesNotReject()rejects()两者都需要一个返回承诺的函数。



Example for when it should reject:

何时应该拒绝的示例:

await rejects(testFunction());

see: https://nodejs.org/api/assert.html#assert_assert_rejects_asyncfn_error_message

请参阅:https: //nodejs.org/api/assert.html#assert_assert_rejects_asyncfn_error_message

Example for when it should not reject:

不应拒绝的示例:

await doesNotReject(testFunction());

see: https://nodejs.org/api/assert.html#assert_assert_doesnotreject_asyncfn_error_message

见:https: //nodejs.org/api/assert.html#assert_assert_doesnotreject_asyncfn_error_message

回答by TLEJMI

This is my Solution for the problem .

这是我的问题解决方案。

    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);

回答by Stefan

I have came with this solution:

我带来了这个解决方案:

import { assert, expect, use } from "chai";
import * as chaiAsPromised from "chai-as-promised";

describe("using chaiAsPromised", () => {
    it("throws an error", async () => {
        await expect(await fails()).to.eventually.be.rejected;
    });
});

回答by Steven Spungin

A no dependency on anything but Mochaexample.

一个任何事情,但摩卡没有依赖性的例子。

Throw a known error, catch all errors, and only rethrow the known one.

抛出一个已知错误,捕获所有错误,并且只重新抛出已知错误。

  it('should throw an error', async () => {
    try {
      await myFunction()
      throw new Error('Expected error')
    } catch (e) {
      if (e.message && e.message === 'Expected error') throw e
    }
  })

If you test for errors often, wrap the code in a custom itfunction.

如果您经常测试错误,请将代码包装在自定义it函数中。

function itThrows(message, handler) {
  it(message, async () => {
    try {
      await handler()
      throw new Error('Expected error')
    } catch (e) {
      if (e.message && e.message === 'Expected error') throw e
    }
  })
}

Then use it like this:

然后像这样使用它:

  itThrows('should throw an error', async () => {
    await myFunction()
  })

回答by karboom

you can write a function to swap resolve & reject handler, and do anything normally

您可以编写一个函数来交换解析和拒绝处理程序,并正常执行任何操作

const promise = new Promise((resolve, rejects) => {
    YourPromise.then(rejects, resolve);
})
const res = await promise;
res.should.be.an("error");

回答by IsaacMiguel

If test your Promised function, in the test must wrap the code inside a try/catch and the expect() must be inside the catch error block

如果测试您的 Promised 函数,则在测试中必须将代码包装在 try/catch 中,并且 expect() 必须在 catch 错误块中

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

So, then in your test

所以,然后在你的测试中

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...');
  }
});

That is my approach, don't know a better way.

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