测试异步函数

Jest中测试异步函数主要有4种方法:

  • test中返回的Promise中断言
  • 使用asyncawait
  • 在回调函数中使用done函数
  • 使用resolvesrejects

返回Promise

如果要测试的函数返回的是一个Promise,那么我们就可以让test返回Promise,在Promise中使用断言。比如:

test('the data is peanut butter', () => {
    return fetchData().then(data => {
        expect(data).toBe('peanut butter');
    })
})

这样,我们就可以测试这个Promise函数了。

注意:我们一定要返回Promise,让Jest知道这是一个异步函数,在Promiseresolve 后进行断言,否则,Jest会提前断言。这个时候,Promise的状态还没有改变,得不到我们想要的结果。如果Promise没有resolve ,而是jest了,那么测试会直接失败。

async/await

在测试异步代码的时候,我们可以直接使用asyncawait。就像下面一样:

test('the data is peanut butter', async () => {
    const data = await fetchData();
    expect(data).toBe('peanut butter');
})

test('the fetch fails with an error', async () => {
    expect.assertions(1);
    try {
        await fetchData();
    } catch (e) {
        expect(e).toMatch('error');
    }
})

在使用async/await测试异步代码的时候,我们要给test函数添加async标记,在异步代码前使用await

当使用async/await测试异步代码失败的时候,记得一定要使用assertions来断言测试有过断言,否则测试不能通过。

使用回调函数

如果我们要测试的异步代码是在回调函数中,而不是在Promise里,那么我们就需要使用test函数中单参数done来解决问题了。比如:

test('the data is peanut butter', done => {
    function callback(error, data) {
        if (error) {
            done(error);
            return;
        }
        try {
            expect(data).toBe('peanut butter');
            done();
        } catch (error) {
            done(error);
        }
    }

    fetchData(callback);
})

在这个例子中,如果done()函数从来没有执行过,那么测试会超时失败。

如果expect执行失败,那么其将抛出一个错误,其后的done 就不会执行。如果我们想要知道具体为什么会失败,那么我们就要在测试中捕获这个错误。所以需要把expect放入try中,然后在catch 中捕获错误,使用done来接收error的值。

使用resolvesrejects

我们也可以让test中的函数返回一个resolvesrejects来测试异步代码。比如:

test('the data is peanut butter', () => {
    return expect(fetchData()).resolves().toBe('peanut butter');
})

直接通过resolves来声明我们期望Promise的状态会变为resolve状态,并返回peanut butter,否则测试失败(无论Promise 是变为reject还是resolve后返回的不是peanut butter)。

同理,通过rejects,我们期望Promise的状态会变为rejects,并返回一个指定的错误。如:

test('the fetch fails with an error', () => {
    return expect(fetchData()).rejects.toMatch('error');
})

测试RxJs中Promise的行为

因为RxJs的实现机制和Promise微任务的问题,在RxJs中不能使用弹珠测试,那么我们就要使用Jest中测试异步代码中的方法来解决问题了。比如:

// Some RxJS code that also consumes a Promise, so TestScheduler won't be able
// to correctly virtualize and the test will always be really asynchronous.
const myAsyncCode = () => from(Promise.resolve('something'));

it('has async code', (done) => {
    myAsyncCode().subscribe((d) => {
        assertEqual(d, 'something');
        done();
    });
});