问题描述
案例是为 uploadFile
编写单元测试,它应该使用 Jest 将文件上传到 Google Storage 存储桶,并在 File 对象上模拟 createReadStream
函数。
my-service.ts
:
async uploadFile(file: FileUpload): Promise<{
url: string
path: string}> {
try {
file.createReadStream().pipe(
bucket
.createWriteStream({
...some options
})
.on('error',(err) => {
reject(err)})
.on('finish',async () => {
resolve({
url: 'file-url',path: 'file-path'
})
})
}
}
my-service.spec.ts
:
describe('#uploadFile',() => {
it('uploads file',async () => {
const bucketMock = new Bucket('the-bucket-mock')
const bucketFileMock = new File(bucketMock,'the-file')
const fileUploadMock = {
filename: 'the-file',mimetype: 'mimetype',encoding: 'encoding',createReadStream: jest.fn().mockImplementation((stream) => {
pipe: jest.fn()
}),}
jest
.spyOn(fileUploadMock,'createReadStream')
.mockImplementation((stream) => {
stream.pipe()
return Promise.resolve({
url: 'url-result',path: 'file-path-result',})
})
const uploadFileResult = await myService.uploadFile(fileUploadMock)
expect(uploadFileResult).toBeCalled()
})
})
解决方法
这部分代码:
createReadStream: jest.fn().mockImplementation((stream) => {
pipe: jest.fn()
}),
没有做你认为它在做的事情。您相信您传递给 mockImplementation
的函数正在返回一个看起来像 {pipe: jest.fn()}
的对象,但是,事实并非如此。如果箭头函数在箭头之后遇到的第一件事是一个开放的大括号,那么现在告诉 TS/JS 你在一个函数体内,它不再有隐式返回。类似于如果您写道:
// Nothing happens here,and it returns undefined
function (stream) {
pipe: jest.fn()
}
对此的解决方法是:
(stream) => {
return { pipe: jest.fn() };
}
或者如果你想保持箭头运算符的简洁性,你只需要确保箭头后面的第一件事不是大括号,即:
// Parenthesis help!
(stream) => ({
pipe: jest.fn()
})