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

时间:2017-08-02 16:17:08

标签: node.js async-await mocha chai chai-as-promised

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

这是一个有效的示例,但我不喜欢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');
});

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

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

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

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

11 个答案:

答案 0 :(得分:16)

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

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

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

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

这是一个简单的例子。与你在问题中所展示的内容没什么不同;我只是使用asyncwins()以及fails()代替expect的{​​{1}}函数。当然,您可以使用返回shouldPromise的函数。

chai.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-promisedrejectedWith等函数的承诺。因此,您必须在eventually.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)
})

正如你所看到的那样,chai断言实际上已经失败了,但是他们在承诺的背景下失败了,没有人$ npm test > mocha-chai-async@1.0.0 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) 编辑或await编辑。因此,Mocha没有看到任何失败,并将测试标记为好像已经通过,但Node.js(如上所述将在未来发生变化的行为)将未处理的拒绝打印到终端。

答案 1 :(得分:3)

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

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

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

答案 2 :(得分:3)

  1. 为chai(npm i chai-as-promised -D)安装chai-as-promised
  2. 只要兑现您的诺言,就不要等待!
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';

chai.use(chaiAsPromised);

const expect = chai.expect;

describe('MY_DESCR', () => {
  it('MY_TEST', async () => {
    expect(myAsyncFunctionThatWillReject()).to.eventually.be.rejected; 
  });
});

答案 3 :(得分: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);

答案 4 :(得分:1)

此示例仅适用于Node!

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


何时应拒绝的示例:

await rejects(testFunction());
  

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

不应拒绝的示例:

await doesNotReject(testFunction());
  

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

答案 5 :(得分: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...');
  }
});

那是我的方法,没有更好的方法。

答案 6 :(得分: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"))
  })
})

答案 7 :(得分:0)

除了Mocha 示例外,没有任何依赖。

抛出一个已知错误,捕获所有错误,然后仅重新抛出该已知错误。

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

如果您经常测试错误,请将代码包装在自定义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
    }
  })
}

然后像这样使用它:

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

答案 8 :(得分:0)

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

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

答案 9 :(得分:0)

我提供了以下解决方案:

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

答案 10 :(得分:0)

另一种方式(适用于异步功能,但不适用于测试中的等待方式)是调用done并声明错误:

it('should throw Error', (done) => {
  myService.myAsyncMethod().catch((e) => {
    try {
      // if you want to check the error message for example
      assert.equal(e.message, 'expected error');
    } catch (assertionError) {
      done(assertionError); // this will fail properly the test
      return; // this prevents from calling twice done()
    }

    done();
  });
});