嘲笑单元测试以监视较低级别的方法(NodeJS)

时间:2019-01-29 04:58:08

标签: node.js unit-testing mocking jestjs spyon

尝试使用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()的重点。想知道我哪里出了错。

2 个答案:

答案 0 :(得分:1)

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

在这种情况下,您传递的是sib,它表示从sendinblue.js导出ES6模块,因此Jest会将{em> module export 替换为{{1} }和间谍一起,并为间谍提供您提供的模拟实现。

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

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

解决此问题的一种简单方法是注意"ES6 modules support cyclic dependencies automatically",因此您可以简单地将模块导入自身,并使用模块导出从sibSubmit内调用sibSubmit

sibSendTransactionalEmail

请注意,用works because Jest transpiles the ES6 modules to Node modules in a way that allows them to be mutated之类的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模块导出

答案 1 :(得分:0)

解决此问题的另一种方法是在模块中重新连接正在监视的功能,这样更好,因为您无需出于测试目的而修改原始代码。如果在ES6之前,可以使用babel-rewire模块,对于ES6,可以使用// 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(); }) );

find