使用 Jest 模拟模块无法正常工作
Mocking a module with Jest not working properly
我正在尝试模拟一个模块,这里是来自 aws-sdk 的 S3。
我唯一能让它工作的方法是这样的:
jest.mock('aws-sdk', () => {
return {
S3: () => ({
putObject: jest.fn()
})
};
});
问题是我无法访问 S3 或 putObject 作为模拟变量,我可以检查是否出于测试目的调用它。
所以我想做这样的事情:
const putObject = jest.fn();
jest.mock('aws-sdk', () => {
return { S3: () => ({ putObject }) };
});
我总是为其他模块这样做并且它工作得很好,它甚至适用于 lambda,但不适用于这种情况。
对我来说,这两个代码看起来完全一样,所以我真的不明白这是怎么回事,为什么它的工作方式不完全一样。
当我在要测试的代码中使用控制台记录 s3 时,我得到:
{ putObject: undefined }
此外,我使用 TypeScript,所以我不能只在测试文件中导入模块,如果它不是模拟变量,我就不能模拟返回值。
感谢您的帮助!
编辑:
这是问题的可重现的最小示例:
uploadFile.ts
import AWS from 'aws-sdk';
const s3 = new AWS.S3();
export const uploadFile = async (key, body) => {
try {
await s3
.putObject({
Bucket: 'myBucket',
Key: key,
Body: body,
})
.promise();
} catch (error) {
console.log(error);
}
};
uploadFile.test.ts
import { uploadFile } from './uploadFile';
const mockPutObject = jest.fn(() => ({ promise: jest.fn() }));
jest.mock('aws-sdk', () => {
return { S3: () => ({ putObject: mockPutObject }) };
});
describe('Test uploadFile', () => {
it('should call putObject', () => {
uploadFile('key', { test: 'test' });
expect(mockPutObject).toHaveBeenCalled();
});
});
我是这样实现的:
import * as AWS from 'aws-sdk';
import { uploadFile } from '.';
const mockPutObject = jest.fn().mockImplementation((data) => {
return {
promise: () => jest.fn()
}
});
jest.mock('aws-sdk', () => {
return {
S3: function () {
return {
putObject: (data: any) => mockPutObject(data)
}
}
};
});
describe('Test uploadFile', () => {
it('should call putObject', () => {
uploadFile('key', { test: 'test' });
expect(mockPutObject).toHaveBeenCalledTimes(1);
expect(mockPutObject).toHaveBeenCalledWith({
Bucket: 'myBucket',
Key: 'key',
Body: {
test: 'test'
},
});
});
});
有一些您可能没有的挑战:
- AWS.S3 是一个具有构造函数的 class,但我找不到有效的 tsconfig 或 babel 配置。因此我将一些
() => {...}
替换为 function () { ... }
- 开玩笑提升属性的方式使 return
mockPutObject
变得棘手,因为它在构建模拟时尚未初始化
- mock 需要 return
promise()
mock 以及与您的 index.ts
一起工作
我正在尝试模拟一个模块,这里是来自 aws-sdk 的 S3。
我唯一能让它工作的方法是这样的:
jest.mock('aws-sdk', () => {
return {
S3: () => ({
putObject: jest.fn()
})
};
});
问题是我无法访问 S3 或 putObject 作为模拟变量,我可以检查是否出于测试目的调用它。
所以我想做这样的事情:
const putObject = jest.fn();
jest.mock('aws-sdk', () => {
return { S3: () => ({ putObject }) };
});
我总是为其他模块这样做并且它工作得很好,它甚至适用于 lambda,但不适用于这种情况。
对我来说,这两个代码看起来完全一样,所以我真的不明白这是怎么回事,为什么它的工作方式不完全一样。
当我在要测试的代码中使用控制台记录 s3 时,我得到:
{ putObject: undefined }
此外,我使用 TypeScript,所以我不能只在测试文件中导入模块,如果它不是模拟变量,我就不能模拟返回值。
感谢您的帮助!
编辑:
这是问题的可重现的最小示例:
uploadFile.ts
import AWS from 'aws-sdk';
const s3 = new AWS.S3();
export const uploadFile = async (key, body) => {
try {
await s3
.putObject({
Bucket: 'myBucket',
Key: key,
Body: body,
})
.promise();
} catch (error) {
console.log(error);
}
};
uploadFile.test.ts
import { uploadFile } from './uploadFile';
const mockPutObject = jest.fn(() => ({ promise: jest.fn() }));
jest.mock('aws-sdk', () => {
return { S3: () => ({ putObject: mockPutObject }) };
});
describe('Test uploadFile', () => {
it('should call putObject', () => {
uploadFile('key', { test: 'test' });
expect(mockPutObject).toHaveBeenCalled();
});
});
我是这样实现的:
import * as AWS from 'aws-sdk';
import { uploadFile } from '.';
const mockPutObject = jest.fn().mockImplementation((data) => {
return {
promise: () => jest.fn()
}
});
jest.mock('aws-sdk', () => {
return {
S3: function () {
return {
putObject: (data: any) => mockPutObject(data)
}
}
};
});
describe('Test uploadFile', () => {
it('should call putObject', () => {
uploadFile('key', { test: 'test' });
expect(mockPutObject).toHaveBeenCalledTimes(1);
expect(mockPutObject).toHaveBeenCalledWith({
Bucket: 'myBucket',
Key: 'key',
Body: {
test: 'test'
},
});
});
});
有一些您可能没有的挑战:
- AWS.S3 是一个具有构造函数的 class,但我找不到有效的 tsconfig 或 babel 配置。因此我将一些
() => {...}
替换为function () { ... }
- 开玩笑提升属性的方式使 return
mockPutObject
变得棘手,因为它在构建模拟时尚未初始化 - mock 需要 return
promise()
mock 以及与您的index.ts
一起工作