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