【问题标题】:Jest with async function calls before and after setTimeout在 setTimeout 之前和之后使用异步函数调用开玩笑
【发布时间】:2020-11-09 14:20:51
【问题描述】:

我有一个主函数,它调用两个异步函数,中间有睡眠函数。这是一个基本示例:

index.js

const func1 = async() => {
    setTimeout(()=>{console.log('func 1...')}, 1000);
}

const func2 = async() => {
    setTimeout(()=>{console.log('func 2...')}, 1000);
}

const sleep = ms => {
    console.log(`Sleeping for ${ms/1000} seconds`);
    return new Promise(resolve => {
        setTimeout(resolve, ms);
    })
}

const main = async() => {
    try {
    await func1();
    // Sleeping for a long long time
    console.log('Before Sleep');
    await sleep(2000000);
    console.log('After Sleep')
    await func2();
    return 'success';
    } catch(err) {
        console.log(err);
        return 'error'
    }
}

这是我的测试代码:

index.test.js

const index = require('./index');

jest.useFakeTimers();

describe('Testing index.js...', () => {
    test('Should return success', async() => {
        const promise = index();
        jest.advanceTimersByTime(2000000);
        promise.then(response => {
            expect(response).toBe('success');
        }) 
    });
})

测试通过,但控制台显示如下:

func 1...
Before Sleep
Sleeping for 2000 seconds

我尝试了同样的方法,但 func1() 和 func2() 是同步函数:

const func1 = () => {
    console.log('func 1...');
}

const func2 = () => {
    console.log('func 2...');

}

const sleep = ms => {
    // Sleeping for a long time
    console.log(`Sleeping for ${ms/1000} seconds`);
    return new Promise(resolve => {
        setTimeout(resolve, ms);
    })
}

const main = async() => {
    try {
    func1();
    // Sleeping for a long long time
    console.log('Before Sleep');
    await sleep(2000000);
    console.log('After Sleep')
    func2();
    return 'success';
    } catch(err) {
        console.log(err);
        return 'error'
    }
}

在这种情况下,测试通过并且日志也符合预期:

func 1...
Before Sleep
Sleeping for 2000 seconds
After Sleep
func 2...

在相同的同步代码中,如果我使 func1 异步(保持 func2 同步),问题会再次出现。 如果 func1 是同步的,而 func2 是异步的,那么一切都会按预期进行。

我也尝试过使用 jest.runAllTimers() 和 jest.runOnlyPendingTimers()。我也尝试在测试文件中使用 async-await,但是(可以理解)给出了超时错误:

index.test.js 使用异步等待

const index = require('./index');

jest.useFakeTimers();

describe('Testing index.js...', () => {
    test('Should return success', async() => {
        const promise = index();
        jest.advanceTimersByTime(3000000);
        const response = await promise;
        expect(response).toBe('success');  
    });
})

控制台

func 1...
Before Sleep
Sleeping for 2000 seconds

错误

Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Timeout

我怎样才能做到这一点? 我开玩笑地尝试了很多 Github 问题的解决方案,还有很多关于堆栈溢出的问题,但似乎没有一个解决方案有效。

我正在使用 jest 25.5.4

编辑:我还尝试将 jest.advanceTimersBytTime() 中的值增加到一天。并且还尝试在 describe async 中创建函数。

【问题讨论】:

  • 我觉得describe函数也需要异步。
  • 为什么会这样?无论如何,我尝试使描述函数异步。结果相同。
  • 这将是一个错误,因为 describe 不应该是异步的。

标签: javascript node.js asynchronous testing jestjs


【解决方案1】:

我最近遇到了类似的问题,对我有用的是从异步调用中推进计时器。似乎 jest 不支持在承诺中设置计时器(请参阅https://github.com/facebook/jest/pull/5171#issuecomment-528752754)。尝试做:

describe('Testing index.js...', () => {
  it('Should return success', () => {    
    const promise = main();
    Promise.resolve().then(() => jest.advanceTimersByTime(2000005));
    return promise.then((res) => {
      expect(res).toBe('success');
    });

  });
});

【讨论】:

  • 这确实解决了 func2 同步时的问题。但是当 func2 为异步时,console.log('After Sleep) 会被执行,但 func2() 中的 console.log() 语句不会被调用。
  • 你说得对,我错过了 - 我会再看看 :)
  • async 不应与done 混合使用。如果断言失败,这将导致测试超时和未被捕获的拒绝。
  • @EstusFlask:已修复,但这仍然不能解决计时器问题。真的不知道为什么func2中setTimeout的回调没有执行。你知道出了什么问题吗?
【解决方案2】:

async、原始承诺和done 回调不应在测试中一起使用。这是开发人员对 Jest 异步测试并不完全满意的一个常见迹象,这会导致测试容易出错。

original 的问题是 promise.then(...) 承诺被忽略,因为它没有被链接。异步测试应该返回一个 Promise 以便它被链接。

func1func2 返回承诺立即解决并产生一滴答延迟而不是一秒延迟。应该考虑到这一点,否则会出现在 setTimeout 之后调用 advanceTimersByTime 的竞争条件。

应该是:

test('Should return success', async() => {
    const promise = index();
    await null; // match delay from await func1() 
    jest.advanceTimersByTime(2000000);
    const response = await promise;
    expect(response).toBe('success');
});

【讨论】:

  • 我是一名新开发人员,我更喜欢异步等待糖而不是 promise.then()。这正是我最初尝试过的,但得到(并且仍然得到)超时错误。这也是我查看的许多 SO 问题之一的解决方案,但似乎在这里不起作用。我在问题中提到了这一点。我将在代码中更突出地包含它。
  • 我更新了帖子。这与另一个答案所暗示的大致相同,但以更有条理的方式。不需要像 2000005 这样的技巧。
  • 还是一样:(。我没明白你说的延迟一滴是什么意思。一滴代表func1中的一行吗?
猜你喜欢
  • 2018-11-23
  • 1970-01-01
  • 1970-01-01
  • 2022-01-01
  • 2019-08-13
  • 1970-01-01
  • 2019-09-11
  • 2016-10-30
  • 1970-01-01
相关资源
最近更新 更多