Jest - 异步例子

2019-08-22  本文已影响0人  小黄人get徐先生

首先,按照入门指南中的文档,在 Jest 中启用 Babel 支持。

让我们实现一个简单的模块,它从 API 获取用户数据并返回用户名。

// user.js
import request from './request';

export function getUserName(userID) {
  return request('/users/' + userID).then(user => user.name);
}

在上面的实现中,我们期望 request.js 模块返回一个 promise。我们链式一个调用到 then 来接收用户名。

现在设想一个 request.js 的实现,它进入网络并获取一些用户数据:

// request.js
const http = require('http');

export default function request(url) {
  return new Promise(resolve => {
    // This is an example of an http request, for example to fetch
    // user data from an API.
    // This module is being mocked in __mocks__/request.js
    http.get({path: url}, response => {
      let data = '';
      response.on('data', _data => (data += _data));
      response.on('end', () => resolve(data));
    });
  });
}

因为我们不想在测试中进入网络,所以我们将在_mocks__文件夹中为request.js模块创建一个手动模拟(该文件夹区分大小写,所以不能使用_mocks__)。它可以是这样的:

// __mocks__/request.js
const users = {
  4: {name: 'Mark'},
  5: {name: 'Paul'},
};

export default function request(url) {
  return new Promise((resolve, reject) => {
    const userID = parseInt(url.substr('/users/'.length), 10);
    process.nextTick(() =>
      users[userID]
        ? resolve(users[userID])
        : reject({
            error: 'User with ' + userID + ' not found.',
          }),
    );
  });
}

现在让我们为异步功能编写一个测试。

// __tests__/user-test.js
jest.mock('../request');

import * as user from '../user';

// The assertion for a promise must be returned.
it('works with promises', () => {
  expect.assertions(1);
  return user.getUserName(4).then(data => expect(data).toEqual('Mark'));

我们调用 jest.mock('../request') 来告诉 Jest 使用我们手动的 mock。它期望返回值是一个将被 resolved 的 Promise。你可以链接任意多的 Promise,并随时调用 expect,只要最后返回一个 Promise。

.resolves

这儿有一种更简洁的方式,使用 resolves 可以获取到一个未包装的 fulfilled promise 的值。如果这个 promise 被 rejected,这个断言将会失败。

it('works with resolves', () => {
  expect.assertions(1);
  return expect(user.getUserName(5)).resolves.toEqual('Paul');
});
async / await

使用 async / await 语法编写测试更加简单。下面是相同的例子此方法的写法:

// async/await can be used.
it('works with async/await', async () => {
  expect.assertions(1);
  const data = await user.getUserName(4);
  expect(data).toEqual('Mark');
});

// async/await can also be used with `.resolves`.
it('works with async/await and resolves', async () => {
  expect.assertions(1);
  await expect(user.getUserName(5)).resolves.toEqual('Paul');
});

要在项目中启动 async/await,安装 @babel/preset-env 并在 babel.config.js 文件中启用该特性。

错误处理

错误可以使用 .catch 方法处理。确保添加了 expect.assertions 确保确定次数的断言调用。不然,一个 fulfilled promise 将不会导致测试失败:

// Testing for async errors using Promise.catch.
test('tests error with promises', () => {
  expect.assertions(1);
  return user.getUserName(2).catch(e =>
    expect(e).toEqual({
      error: 'User with 2 not found.',
    }),
  );
});

// Or using async/await.
it('tests error with async/await', async () => {
  expect.assertions(1);
  try {
    await user.getUserName(1);
  } catch (e) {
    expect(e).toEqual({
      error: 'User with 1 not found.',
    });
  }
});
.rejects

这个 .rejects 工作原理类似 .resolves。如果这个 promise 是 fulfilled,这个测试将自动失败。

// Testing for async errors using `.rejects`.
it('tests error with rejects', () => {
  expect.assertions(1);
  return expect(user.getUserName(3)).rejects.toEqual({
    error: 'User with 3 not found.',
  });
});

// Or using async/await with `.rejects`.
it('tests error with async/await and rejects', async () => {
  expect.assertions(1);
  await expect(user.getUserName(3)).rejects.toEqual({
    error: 'User with 3 not found.',
  });
});

参考:

expect.assertions(number) 期待断言的次数
expect.assertions(number) 验证在测试期间调用一定数量的断言。这在测试异步代码时非常有用,以确保回调中的断言确实被调用了。

例如,我们有一个函数 doAsync 接收两个回调 callback1callback2 ,它们将以未知的顺序异步调用它们。我们可以这样测试:

test('doAsync calls both callbacks', () => {
  expect.assertions(2); // 期待发生两次断言
  function callback1(data) {
    expect(data).toBeTruthy();
  }
  function callback2(data) {
    expect(data).toBeTruthy();
  }

  doAsync(callback1, callback2);
});

expect.assertions(2) 确保实际调用了两个回调。

上一篇 下一篇

猜你喜欢

热点阅读