尝试使用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: '[email protected]', name: 'Something' }],
      to: [{ email: '[email protected]', 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()的重点。想知道我哪里出错了。

最佳答案

jest.spyOn替换method on the object it is passed with a spy

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

mail.send然后调用sibSendTransactionalEmail,然后直接调用sibSubmit

换句话说,不会调用您的间谍,因为sibSendTransactionalEmail不会调用sibSubmit的模块导出,而只是直接调用sibSubmit

解决此问题的一种简单方法是注意"ES6 modules support cyclic dependencies automatically",因此您可以简单地将模块导入自身,并使用模块导出从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


请注意,用jest.spyOn代替ES6模块导出,例如works because Jest transpiles the ES6 modules to Node modules in a way that allows them to be mutated

10-05 23:37