在 Jest 中,如何模拟具有依赖项的 TypeScript 类?
Posted
技术标签:
【中文标题】在 Jest 中,如何模拟具有依赖项的 TypeScript 类?【英文标题】:In Jest, how do you mock a TypeScript class with dependencies? 【发布时间】:2020-10-17 13:56:37 【问题描述】:鉴于这个相对人为的类从 Elasticsearch(或任何数据存储区)获取单个记录:
export default class UserRepoImpl implements UserRepo
constructor(private readonly esClient: ElasticsearchClient)
public async getUser(id: string): Promise<User>
const response = await this.esClient.request('GET', `/users/_doc/$id`);
return response._source;
如何编写一个测试来为我实例化一个UserRepoImpl
,但插入一个模拟的ElasticsearchClient作为构造函数的参数?
【问题讨论】:
【参考方案1】:这里是单元测试解决方案:
userRepoImpl.ts
:
interface User
interface UserRepo
getUser(id: string): Promise<User>;
interface ElasticsearchClient
request(method: string, endpoint: string): Promise< _source: any >;
export default class UserRepoImpl implements UserRepo
constructor(private readonly esClient: ElasticsearchClient)
public async getUser(id: string): Promise<User>
const response = await this.esClient.request('GET', `/users/_doc/$id`);
return response._source;
userRepoImpl.test.ts
:
import UserRepoImpl from './userRepoImpl';
describe('62603645', () =>
it('should pass', async () =>
const mUser = name: 'wen' ;
const mEsClient = request: jest.fn().mockResolvedValueOnce( _source: mUser ) ;
const userRepoImpl = new UserRepoImpl(mEsClient);
const actual = await userRepoImpl.getUser('1');
expect(actual).toEqual(mUser);
expect(mEsClient.request).toBeCalledWith('GET', '/users/_doc/1');
);
);
带有覆盖率报告的单元测试结果:
PASS ***/62603645/userRepoImpl.test.ts (10.988s)
62603645
✓ should pass (5ms)
-----------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
userRepoImpl.ts | 100 | 100 | 100 | 100 |
-----------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 12.27s
【讨论】:
感谢您非常注重细节的解决方案。你知道你的变量mClient
和client
之间是否有区别(@teneff提供的解决方案)
我在使用模拟客户端实例化新实例时遇到错误:Argument of type ' request: Mock<any, any>; ' is not assignable to parameter of type 'ElasticsearchClient'. Type ' request: Mock<any, any>; ' is missing the following properties from type 'ElasticsearchClient': awsHttps, endpoint
【参考方案2】:
您可以像这样在测试中创建模拟客户端:
const client: jest.Mocked<ElasticsearchClient> =
request: jest.fn()
它会在传递时静态检查解析值的类型:
示例测试:
describe('UserRepoImpl', () =>
describe('given a client', () =>
const client: jest.Mocked<ElasticsearchClient> =
request: jest.fn()
describe('getUser', () =>
beforeAll(() =>
client.request.mockResolvedValue(
_source:
id: '3'
)
)
it('should return user by Id', async () =>
const userRepo = new UserRepoImpl(client)
await expect(userRepo.getUser("3")).resolves.toEqual(
id: '3'
)
)
)
)
)
【讨论】:
我是await expect
。
resolves
本质上是异步的。同步断言原生 promise 解析的内容在物理上是不可能的。一个成功的resolves
断言返回的承诺被忽略,一个失败的承诺不会使测试失败,但会导致未捕获的承诺拒绝。
@EstusFlask 同意。同样返回一个承诺也会有同样的结果
@Teneff 当我模拟客户端时,我收到错误:Type ' request: Mock<any, any>; ' is not assignable to type 'Mocked<ElasticsearchClient>'. Type ' request: Mock<any, any>; ' is missing the following properties from type 'ElasticsearchClient': awsHttps, endpoint
@JeffreyWen 检查您是否使用最新的 jest 和 @types/jest 软件包版本以上是关于在 Jest 中,如何模拟具有依赖项的 TypeScript 类?的主要内容,如果未能解决你的问题,请参考以下文章
在 Java Play 2.4 中测试具有模拟依赖项的控制器
如何为我的 Google 地图应用使用具有 Google Play 服务依赖项的非 Nexus 5 模拟器