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

后端 未结 11 769
花落未央
花落未央 2020-12-14 05:50

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

Here\'s an example that works, but I dislike t

相关标签:
11条回答
  • 2020-12-14 06:37

    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

    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.

    0 讨论(0)
  • 2020-12-14 06:39

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

    await resolves a Promise. If the Promise rejects, it throws the rejected value.

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

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

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

    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:

    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-promised returns promises for its functions such as rejectedWith and eventually.something. Therefore you must await them in the context of an async test function, or else failing conditions will still pass:

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

    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.

    0 讨论(0)
  • 2020-12-14 06:39

    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"))
      })
    })
    
    0 讨论(0)
  • 2020-12-14 06:42

    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);
    
    0 讨论(0)
  • 2020-12-14 06:43

    This example only works with Node!

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


    Example for when it should reject:

    await rejects(testFunction());
    

    see: 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

    0 讨论(0)
  • 2020-12-14 06:43

    Another way (applicable to an async function, but not using await in the test) is calling done with an assertion error:

    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();
      });
    });
    
    0 讨论(0)
提交回复
热议问题