1

尝试使用 Jest 监视和覆盖两个级别的函数。

测试结果显示,“预期的模拟函数已被调用,但没有被调用。”

// mail/index.unit.test.js
import mail from './index';
import * as sib from '../sendinblue';

describe('EMAIL Util', () =>
  test('should call sibSubmit in server/utils/sendinblue/index.js', async() => {
    const sibMock = jest.spyOn(sib, 'sibSubmit');
    sibMock.mockImplementation(() => 'Calling sibSubmit()');
    const testMessage = {
      sender: [{ email: 'foo@example.com', name: 'Something' }],
      to: [{ email: 'foo@example.com', name: 'Something' }],
      subject: 'My Subject',
      htmlContent: 'This is test content'
    };
    await mail.send(testMessage);
    expect(sibMock).toHaveBeenCalled();
  })
);

mail.send() 来自这里...

// mail/index.js
import { sibSendTransactionalEmail } from '../sendinblue';

export default {
  send: async message => {
    try {
      return await sibSendTransactionalEmail(message);
    } catch(err) {
      console.error(err);
    }
  }
};

它通过 axios 使用 SendInBlue 的 API(为什么我需要模拟)...

// sendinblue/index.js
import axios from 'axios';
import config from '../../config/environment';

export async function sibSubmit(method, url, data) {
  let instance = axios.create({
    baseURL: 'https://api.sendinblue.com',
    headers: { 'api-key': config.mail.apiKey }
  });
  try {
    const response = await instance({
      method,
      url,
      data
    });
    return response;
  } catch(err) {
    console.error('Error communicating with SendInBlue', instance, err);
  }
}

export const sibSendTransactionalEmail = message => sibSubmit('POST', '/v3/smtp/email', message);

我假设 mail.send() 会在另一个模块中调用 sibSendTransactionalEmail(),它会调用 sibSubmit(),这是 jest.spyOn() 的焦点。想知道我哪里出错了。

4

2 回答 2

2

jest.spyOn用 spy替换它传递的对象上的方法

在这种情况下,您将传递sib代表 ES6 模块导出的 spy sendinblue.js,因此Jest将用 spy 替换模块导出sibSubmit并为 spy 提供您提供的模拟实现。

mail.sendthen 调用sibSendTransactionalEmail which thensibSubmit直接调用。

换句话说,你的间谍没有被调用,因为sibSendTransactionalEmail没有调用模块导出sibSubmit它只是sibSubmit直接调用。

解决此问题的一种简单方法是注意“ES6 模块自动支持循环依赖”,因此您可以简单地将模块导入自身并使用模块导出sibSubmit从内部调用:sibSendTransactionalEmail

import axios from 'axios';
import config from '../../config/environment';
import * as sib from './';  // import module into itself

export async function sibSubmit(method, url, data) {
  let instance = axios.create({
    baseURL: 'https://api.sendinblue.com',
    headers: { 'api-key': config.mail.apiKey }
  });
  try {
    const response = await instance({
      method,
      url,
      data
    });
    return response;
  } catch(err) {
    console.error('Error communicating with SendInBlue', instance, err);
  }
}

export const sibSendTransactionalEmail = message => sib.sibSubmit('POST', '/v3/smtp/email', message);  // call sibSubmit using the module export

请注意,用这样的方式替换 ES6 模块导出jest.spyOn是可行的,因为Jest将 ES6 模块转换为Node模块以允许它们被变异

于 2019-01-29T12:24:01.450 回答
0

解决此问题的另一种方法是重新连接您在模块中监视的函数,这更好,因为您不必为了测试目的而修改原始代码。如果在 ES6 之前或对于 ES6 ,您可以使用该rewire模块:babel-rewire

// mail/index.unit.test.js
import mail from './index';
import * as sib from '../sendinblue';

describe('EMAIL Util', () =>
  test('should call sibSubmit in server/utils/sendinblue/index.js', async() => {
    const sibMock = jest.spyOn(sib, 'sibSubmit');
    sibMock.mockImplementation(() => 'Calling sibSubmit()');
    //============ force the internal calls to use the mock also
    sib.__set__("sibSubmit", sibMock);  
    //============
    const testMessage = {
      sender: [{ email: 'foo@example.com', name: 'Something' }],
      to: [{ email: 'foo@example.com', name: 'Something' }],
      subject: 'My Subject',
      htmlContent: 'This is test content'
    };
    await mail.send(testMessage);
    expect(sibMock).toHaveBeenCalled();
  })
);
于 2019-09-21T15:58:54.030 回答