使用 Jest 监视和模拟节点模块中 ctors 和方法的 return 值
Spying & mocking return values on ctors & methods in node module with Jest
经过很多 trial/error,在这里搜索 SO,并展示我的 Google Fu,认输并寻求帮助。
TL/DR -
尝试正确模拟节点模块,更改内部方法 return 类型,并监视节点模块内的 ctor 和方法调用。
我的具体情况是测试 Microsoft Azure 存储 blob SDK @azure/storage-blob,但问题并不特定于此包。这只是一个很好的例子,因为 4 个 LOC 的捕获实现了一项任务(将文件上传到存储容器),因为其中 2-3 个 LOC 涵盖了 4 个场景。这是我要测试的代码,并附有关于我到底想测试什么的评论:
export async function saveImage(account: string, container: string, imageBuffer: Buffer, imageName: string): Promise<void> {
try {
// init storage client
// (1) want to spy on args passed into ctor
const blobServiceClient: BlobServiceClient = new BlobServiceClient(`https://${account}.blob.core.windows.net`, new DefaultAzureCredential());
// init container
// (2) same as #1
const containerClient: ContainerClient = await blobServiceClient.getContainerClient(container);
// init block blob client
// (3) same as #1 & #2
const blockBlobClient: BlockBlobClient = containerClient.getBlockBlobClient(imageName);
// save file
// (4) same as #1,2, & 3
// (5) manipulate returned value
// (6) throw cause method to internally throw error
await blockBlobClient.upload(imageBuffer, imageBuffer.length, { blobHTTPHeaders: { blobContentType: 'image/png' } });
return Promise.resolve();
} catch (err: Error) {
return Promise.reject(err);
}
}
我在 ./__mocks/@azure/storage-blob.ts 中为模块设置了一个手动模拟,如下所示:
const MockStorageBlob = jest.createMockFromModule('@azure/storage-blob');
/**
* Utility method throw exception in the `BlockBlobClient.upload()` method.
*/
(MockStorageBlob as any).__setBlockBlobUpload_toFail = () => {
(MockStorageBlob as any).BlobServiceClient = jest.fn().mockImplementation(() => {
return {
getContainerClient: jest.fn().mockReturnValue({
getBlockBlobClient: jest.fn().mockReturnValue({
upload: jest.fn().mockImplementation(() => {
throw new Error('synthetic error');
})
})
})
}
});
}
module.exports = MockStorageBlob;
在我的测试中,我可以像这样成功测试上面的#6:
import {
BlockBlobClient,
BlockBlobUploadResponse
} from '@azure/storage-blob';
import { saveImageToCDN as functionUnderTest } from './saveImageToCDN';
// mock Azure Storage blob NPM package
jest.mock('@azure/storage-blob');
describe('check expected with failure', () => {
beforeEach((done) => {
// reset number of times things have been called
jest.clearAllMocks();
done();
});
test(`it calls 'trackException()' when upload throws exception`, async (done) => {
expect.assertions(1);
// setup test
require('@azure/storage-blob').__setBlockBlobUpload_toFail();
// run SUT
const imageBuffer = Buffer.from('test string');
functionUnderTest(imageBuffer, 'imageName.png')
.then(() => {
expect(new Error('should not reach this')).toBeUndefined();
})
.catch((err: Error) => {
expect(err).toBeDefined();
})
.finally(() => {
done();
});
});
});
...但我想不出正确的语法来监视 upload()
方法(#4),或者我试图测试的任何其他东西(#1- 5).如果重要,请在 Node v14 上使用 Jest v26。
__setBlockBlobUpload_toFail
函数 return 可以引用模拟函数吗?
那会给出这样的东西:
const MockStorageBlob = jest.createMockFromModule('@azure/storage-blob');
/**
* Utility method throw exception in the `BlockBlobClient.upload()` method.
*/
(MockStorageBlob as any).__setBlockBlobUpload_toFail = () => {
const upload = jest.fn().mockImplementation(() => {
throw new Error('synthetic error');
});
const getBlockBlobClient = jest.fn().mockReturnValue({ upload });
const getContainerClient = jest.fn().mockReturnValue({ getBlockBlobClient });
const BlobServiceClient = jest.fn().mockImplementation(() => {
return {
getContainerClient
}
});
(MockStorageBlob as any).BlobServiceClient = BlobServiceClient;
return {
upload,
getBlockBlobClient,
getContainerClient,
BlobServiceClient
};
}
module.exports = MockStorageBlob;
在您的测试中,您将像这样检索它们:
// setup test
const mockFns = require('@azure/storage-blob').__setBlockBlobUpload_toFail();
// run SUT
const imageBuffer = Buffer.from('test string');
functionUnderTest(imageBuffer, 'imageName.png')
.then(() => {
expect(new Error('should not reach this')).toBeUndefined();
})
.catch((err: Error) => {
expect(mockFns.getBlockBlobClient.mock.calls[0][0]).toBe('imageName.png')
expect(err).toBeDefined();
})
.finally(() => {
done();
});
经过很多 trial/error,在这里搜索 SO,并展示我的 Google Fu,认输并寻求帮助。
TL/DR - 尝试正确模拟节点模块,更改内部方法 return 类型,并监视节点模块内的 ctor 和方法调用。
我的具体情况是测试 Microsoft Azure 存储 blob SDK @azure/storage-blob,但问题并不特定于此包。这只是一个很好的例子,因为 4 个 LOC 的捕获实现了一项任务(将文件上传到存储容器),因为其中 2-3 个 LOC 涵盖了 4 个场景。这是我要测试的代码,并附有关于我到底想测试什么的评论:
export async function saveImage(account: string, container: string, imageBuffer: Buffer, imageName: string): Promise<void> {
try {
// init storage client
// (1) want to spy on args passed into ctor
const blobServiceClient: BlobServiceClient = new BlobServiceClient(`https://${account}.blob.core.windows.net`, new DefaultAzureCredential());
// init container
// (2) same as #1
const containerClient: ContainerClient = await blobServiceClient.getContainerClient(container);
// init block blob client
// (3) same as #1 & #2
const blockBlobClient: BlockBlobClient = containerClient.getBlockBlobClient(imageName);
// save file
// (4) same as #1,2, & 3
// (5) manipulate returned value
// (6) throw cause method to internally throw error
await blockBlobClient.upload(imageBuffer, imageBuffer.length, { blobHTTPHeaders: { blobContentType: 'image/png' } });
return Promise.resolve();
} catch (err: Error) {
return Promise.reject(err);
}
}
我在 ./__mocks/@azure/storage-blob.ts 中为模块设置了一个手动模拟,如下所示:
const MockStorageBlob = jest.createMockFromModule('@azure/storage-blob');
/**
* Utility method throw exception in the `BlockBlobClient.upload()` method.
*/
(MockStorageBlob as any).__setBlockBlobUpload_toFail = () => {
(MockStorageBlob as any).BlobServiceClient = jest.fn().mockImplementation(() => {
return {
getContainerClient: jest.fn().mockReturnValue({
getBlockBlobClient: jest.fn().mockReturnValue({
upload: jest.fn().mockImplementation(() => {
throw new Error('synthetic error');
})
})
})
}
});
}
module.exports = MockStorageBlob;
在我的测试中,我可以像这样成功测试上面的#6:
import {
BlockBlobClient,
BlockBlobUploadResponse
} from '@azure/storage-blob';
import { saveImageToCDN as functionUnderTest } from './saveImageToCDN';
// mock Azure Storage blob NPM package
jest.mock('@azure/storage-blob');
describe('check expected with failure', () => {
beforeEach((done) => {
// reset number of times things have been called
jest.clearAllMocks();
done();
});
test(`it calls 'trackException()' when upload throws exception`, async (done) => {
expect.assertions(1);
// setup test
require('@azure/storage-blob').__setBlockBlobUpload_toFail();
// run SUT
const imageBuffer = Buffer.from('test string');
functionUnderTest(imageBuffer, 'imageName.png')
.then(() => {
expect(new Error('should not reach this')).toBeUndefined();
})
.catch((err: Error) => {
expect(err).toBeDefined();
})
.finally(() => {
done();
});
});
});
...但我想不出正确的语法来监视 upload()
方法(#4),或者我试图测试的任何其他东西(#1- 5).如果重要,请在 Node v14 上使用 Jest v26。
__setBlockBlobUpload_toFail
函数 return 可以引用模拟函数吗?
那会给出这样的东西:
const MockStorageBlob = jest.createMockFromModule('@azure/storage-blob');
/**
* Utility method throw exception in the `BlockBlobClient.upload()` method.
*/
(MockStorageBlob as any).__setBlockBlobUpload_toFail = () => {
const upload = jest.fn().mockImplementation(() => {
throw new Error('synthetic error');
});
const getBlockBlobClient = jest.fn().mockReturnValue({ upload });
const getContainerClient = jest.fn().mockReturnValue({ getBlockBlobClient });
const BlobServiceClient = jest.fn().mockImplementation(() => {
return {
getContainerClient
}
});
(MockStorageBlob as any).BlobServiceClient = BlobServiceClient;
return {
upload,
getBlockBlobClient,
getContainerClient,
BlobServiceClient
};
}
module.exports = MockStorageBlob;
在您的测试中,您将像这样检索它们:
// setup test
const mockFns = require('@azure/storage-blob').__setBlockBlobUpload_toFail();
// run SUT
const imageBuffer = Buffer.from('test string');
functionUnderTest(imageBuffer, 'imageName.png')
.then(() => {
expect(new Error('should not reach this')).toBeUndefined();
})
.catch((err: Error) => {
expect(mockFns.getBlockBlobClient.mock.calls[0][0]).toBe('imageName.png')
expect(err).toBeDefined();
})
.finally(() => {
done();
});