gpt4 book ai didi

javascript - Jest 测试上下文/监视在函数外部创建的模拟变量(类级别)

转载 作者:行者123 更新时间:2023-11-28 20:26:23 24 4
gpt4 key购买 nike

我正尝试在 Jest 中进行一些测试,但遇到了模拟/ spy 问题。我已经设法使测试正常工作,但只能通过更改我的实现(我对此感到不快)。

这是测试:

import * as postmark from 'postmark';
jest.mock('postmark');

const mockGetServers = jest.fn();
const AccountClient = jest.fn(() => {
return {
getServers: mockGetServers
};
});
postmark.AccountClient = AccountClient;

import accountApi from './account-api';

describe('account-api', () => {
describe('listServers', () => {
it('calls postmark listServers', async () => {
await accountApi.listServers();

expect(mockGetServers).toHaveBeenCalledTimes(1);
});
});
});

这是工作实现:

import * as postmark from 'postmark';
const accountToken = 'some-token-number';

const listServers = async () => {
try {
const accountClient = postmark.AccountClient(accountToken);
const servers = await accountClient.getServers();
return servers;
} catch (e) {
console.log('ERROR', e);
}
};

export default {
listServers
}

这是原始实现:

import * as postmark from 'postmark';
const accountToken = 'some-token-number';
const accountClient = postmark.AccountClient(accountToken);

const listServers = async () => {
try {
const servers = await accountClient.getServers();
return servers;
} catch (e) {
console.log('ERROR', e);
}
};

export default {
listServers
}

唯一的变化是在代码中创建 accountClient 的位置(在 listServers 函数内部或外部)。最初的实现会完成,jest 会报告模拟没有被调用。

我很困惑为什么这不起作用并猜测它与模拟的上下文有关。我是否遗漏了 jest 在引擎盖下的工作方式?由于 accountApi 的实现将有更多的功能都使用同一个客户端,因此为所有功能而不是每个功能创建一个客户端是有意义的。我不适合按功能创建它。

我创建 accountClient 的方式有何不同,这意味着可以在测试中监视模拟?有没有一种方法可以模拟(和监视)在类级别而不是函数级别创建的对象?

谢谢

最佳答案

Am I missing something about the way jest works under the hood?

需要注意两点:

  1. ES6 import 调用 are hoisted to the top of the current scope
  2. babel-jest 提升对 jest.mock 的调用 to the top of their code block (最重要的是,包括 block 中的任何 ES6 import 调用)

What is different about the way I have created the accountClient that means the mock can be spied on in the test?

在这两种情况下都先运行:

jest.mock('postmark');

...这将自动模拟 postmark 模块。

然后运行:

import accountApi from './account-api';

原始实现中,这一行运行:

const accountClient = postmark.AccountClient(accountToken);

...捕获调用 postmark.AccountClient 的结果并将其保存在 accountClient 中。 postmark 的自动模拟将使用返回 undefined 的模拟函数 stub AccountClient,因此 accountClient 将是设置为 undefined

在这两种情况下,测试代码现在开始运行,为 postmark.AccountClient 设置模拟。

然后在测试期间运行此行:

await accountApi.listServers();

原始实现中,该调用最终运行如下:

const servers = await accountClient.getServers();

...由于 accountClient 未定义,它会掉落到 catch,记录错误,并继续测试,直到它在此行失败:

expect(mockGetServers).toHaveBeenCalledTimes(1);

...因为从未调用过mockGetServers

另一方面,在工作实现中运行:

const accountClient = postmark.AccountClient(accountToken);
const servers = await accountClient.getServers();

...并且由于 postmark 在这一点上被模拟,所以它使用模拟并且测试通过。


Is there a way I can mock (and spy on) the object that is created at class level not at function level?

是的。

因为 原始实现 捕获调用 postmark.AccountClient 的结果 一旦它被导入,你只需要确保你的模拟是在您导入原始实现之前设置的。

最简单的方法之一是使用 module factory 设置模拟在调用 jest.mock 期间,因为它首先被提升并运行。

这是一个更新的测试,适用于原始实现:

import * as postmark from 'postmark';
jest.mock('postmark', () => { // use a module factory
const mockGetServers = jest.fn();
const AccountClient = jest.fn(() => {
return {
getServers: mockGetServers // NOTE: this returns the same mockGetServers every time
};
});
return {
AccountClient
}
});

import accountApi from './account-api';

describe('account-api', () => {
describe('listServers', () => {
it('calls postmark listServers', async () => {
await accountApi.listServers();

const mockGetServers = postmark.AccountClient().getServers; // get mockGetServers
expect(mockGetServers).toHaveBeenCalledTimes(1); // Success!
});
});
});

关于javascript - Jest 测试上下文/监视在函数外部创建的模拟变量(类级别),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55047828/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com