开玩笑的单元测试以监视较低级别的方法(NodeJS)
Jest unit test to spy on lower-level method (NodeJS)
尝试使用 Jest 监视和重写函数的下一级。
测试结果说,"Expected mock function to have been called, but it was not called."
// 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() 的焦点。想知道我哪里错了。
jest.spyOn
replaces the 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",这样您就可以简单地将模块导入自身并使用模块导出从 sibSendTransactionalEmail
中调用 sibSubmit
:
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 模块导出像这样
另一种解决此问题的方法是在模块中重新连接您正在监视的函数,这更好,因为您不必为了测试目的而修改原始代码。如果在 ES6 之前,您可以使用 rewire
模块,对于 ES6,您可以使用 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();
})
);
尝试使用 Jest 监视和重写函数的下一级。
测试结果说,"Expected mock function to have been called, but it was not called."
// 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() 的焦点。想知道我哪里错了。
jest.spyOn
replaces the 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",这样您就可以简单地将模块导入自身并使用模块导出从 sibSendTransactionalEmail
中调用 sibSubmit
:
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 模块导出像这样
另一种解决此问题的方法是在模块中重新连接您正在监视的函数,这更好,因为您不必为了测试目的而修改原始代码。如果在 ES6 之前,您可以使用 rewire
模块,对于 ES6,您可以使用 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();
})
);