Как проверить, была ли вызвана функция A внутри тестовой функции B, если она была вызвана асинхронно

Таким образом, в основном у меня есть функция, которую я хочу протестировать, и которую мы будем называть функцией A. Я хочу проверить, была ли вызвана функция B внутри функции A. Проблема в том, что функция B вызывается внутри функции A асинхронно через разрешенный Promise. Это приведет к сбою утверждения sinon, потому что тест завершится до вызова функции B!

Вот сценарий рабочего кода.

const sinon = require('sinon');

describe('functionToBeTested', () => {
  it('someFunction is called', () => {
    // spy on the function we need to check if called
    const spy = sinon.spy(someClass.prototype, 'someFunction');
    // call the function being tested
    functionToBeTested();
    // check if spy was called
    sinon.assert.called(spy);
  });
});

class someClass {
  someFunction() {
    console.log('Hello');
  }
}

const somePromise = Promise.resolve();

function functionToBeTested() {
  const instance = new someClass();
  // some synchronous code here
  // if instance.someFunction() is called here the test will pass
  // .
  // .
  // .
  somePromise.then(() => {
    instance.someFunction();
    // the function is called and Hello is printed but the test will fail
  })
  // .
  // .
  // .
  // some synchronous code here
  // if instance.someFunction() is called here the test will pass
} 

1 ответ

Ваш пример немного нетрадиционен. У вас есть functionToBeTested, который имеет двойственное поведение (синхронизация и асинхронность одновременно). Когда вы тестируете этот метод, поведение должно быть хорошо известно и стандартизировано заранее, чтобы вы могли соответствующим образом структурировать тест и утверждения.

Проблема в этом сценарии заключается в том, что вы пытаетесь проверить поведение функции в режиме синхронизации, хотя внутренние части работают в режиме " забыл и забыл" - т.е. нет зависимости от результата метода instance.someFunction(),

Если functionToBeTested() вернул обещание - таким образом, будучи асинхронным по дизайну, это было бы просто для вашего тестового сценария. Но в этом случае вам также потребуется нестандартный подход к тестированию. Это означает, что если вы делаете что-то вроде:

describe('functionToBeTested', () => {

    it('someFunction is called', (done) => {

        // spy on the function we need to check if called
        const spy = sinon.spy(SomeClass.prototype, 'someFunction');

        // call the function being tested
        functionToBeTested();

        setTimeout(() => {
            // check if spy was called
            sinon.assert.called(spy);
            done();
        }, 10);

    });
});    

тест пройдёт. Здесь произошло то, что мы объявили тест асинхронным, используя аргумент done в обратном вызове. Кроме того, мы добавили таймер для имитации задержки перед проверкой, был ли вызван шпион.

Поскольку вызов "запустии забудь" только распечатывает сообщение, достаточно подождать 10 мс. Если выполнение обещания заняло больше времени, время ожидания должно быть скорректировано.

Как указывалось ранее, нетрадиционные реализации требуют нетрадиционных подходов. Я бы посоветовал вам пересмотреть свои требования и переделать решение.

Другие вопросы по тегам