Убедитесь, что исключение выбрано с использованием Mocha/Chai и async/wait

Я изо всех сил пытаюсь разработать лучший способ проверить, что обещание отклонено в тесте Mocha при использовании async/wait.

Вот пример, который работает, но мне не нравится, что should.be.rejectedWith возвращает обещание, которое должно быть возвращено из тестовой функции для правильной оценки. Использование async/await устраняет это требование для тестирования значений (как я делаю для результата wins() ниже), и я чувствую, что, вероятно, я забуду оператор возврата в какой-то момент, и в этом случае тест всегда будет проходить,

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

Ответ 1

Проблема с этим подходом заключается в том, что (await fails()).should.throw(Error) не имеет смысла.

await разрешает Promise. Если Promise отвергает, он выбрасывает отклоненное значение.

Итак, (await fails()).should.throw(Error) никогда не может работать: если fails() отклоняется, возникает ошибка, и .should.throw(Error) никогда не выполняется.

Самый идиоматический вариант, который у вас есть, - использовать свойство Chai rejectedWith, как вы показали в своем вопросе.

Вот краткий пример. Не так много отличается от того, что вы продемонстрировали в своем вопросе; Я просто использую async функции для wins() и fails() и expect а не should. Конечно, вы можете использовать функции, которые возвращают Promise и 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-promised возвращения обещают его функции, такие как rejectedWith и в 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)
})

Если вы проверили тесты с приведенным выше кодом, вы получите следующее:

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

Как вы можете видеть, утверждения хаи на самом деле потерпели неудачу, но им не удалось в контексте обещания, что никто никогда не await или не catch. Таким образом, Mocha не видит сбоя и отмечает тесты, как если бы они проходили, но Node.js (в поведении, которое изменится в будущем, как указано выше) печатает необработанные отклонения к терминалу.

Ответ 2

Это мое решение проблемы.

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

Ответ 3

Я использую пользовательскую функцию, как это:

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

Ответ 4

Этот пример работает только с Node!

Когда вы используете Mocha в Node.js, вы можете использовать doesNotReject() или rejects(), для обоих требуется функция, которая возвращает обещание.


Пример того, когда он должен отклонить:

await rejects(testFunction());

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

Пример того, когда он не должен отклонять:

await doesNotReject(testFunction());

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

Ответ 5

Если вы тестируете свою функцию Promised, в тесте необходимо обернуть код внутри try/catch, а ожидаемый() должен быть внутри блока ошибки 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

Вы можете использовать async/await и should для простой проверки

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