用于链式函数调用的 Jest 单元测试
Posted
技术标签:
【中文标题】用于链式函数调用的 Jest 单元测试【英文标题】:Jest Unit Test for chained function calls 【发布时间】:2020-10-01 00:33:57 【问题描述】:我已经为此苦苦挣扎了一段时间。单元测试新手.. 我正在尝试模拟单元测试用例 -
fetchFooter()
client.items().type('footer_link')
.depthParameter(10)
.toObservable()
.subscribe((response) =>
this.setState(
resFooter: response.items,
resFooterLinked: response.linkedItems,
);
);
客户在哪里
import DeliveryClient from '@kentico/kontent-delivery';
const client = new DeliveryClient(
projectId: <projectKey>,
enableAdvancedLogging: false,
);
这是我到目前为止写的,但似乎不起作用。
const sample =
items: ,
linkedItems:
;//this is my response object
describe("testing client", () =>
const mockClient =
items: jest.fn().mockReturnThis(),
type: jest.fn().mockReturnThis(),
depthParameter: jest.fn().mockReturnThis(),
toObservable: jest.fn().mockReturnThis(),
subscribe: jest.fn().mockReturnThis(),
;
const mockProps =
client: mockClient,
;
const component = mount(
<Footer mockProps=mockProps resFooter=true resFooterLinked=true />
);
describe("Component", () =>
describe("#componentDidMount", () =>
it("should mount the component and set state correctly", () =>
const mockedResponse = sample;
mockClient.subscribe.mockImplementationOnce((handler) => handler(sample));
// tslint:disable-next-line: no-string-literal
component["setState"] = jest.fn();
component.instance().fetchFooter();
expect(
mockClient.items().type().depthParameter().toObservable().subscribe
).toBeCalledWith("footer_link");
// tslint:disable-next-line: no-string-literal
expect(component["setState"]).toBeCalledWith(sample);
);
);
);
);
我收到一个错误 -
expect(jest.fn()).toBeCalledWith(...expected)
Expected: "footer_link"
Number of calls: 0
也许我在这里缺少一些基础知识。我为此寻找解决方案,但不幸的是没有找到适合我的解决方案。 有人能帮忙吗?提前致谢
【问题讨论】:
【参考方案1】:正如the reference 所说,mockReturnThis
是:
jest.fn(function ()
return this;
);
它只使函数可链接,并且不会将参数传递给链中的下一个函数。 subscribe
不应该被 footer_link
调用,而是 type
被这个参数调用。
没有必要让模拟的subscribe
调用处理程序,这并不能保证在其中调用了setState
。另外,setState
不是用sample
调用的,而是用另一个对象调用的。
没有必要在测试中调用expect(mockClient.items()...)
chain,这只会增加调用次数。调用断言不允许确定调用方法的顺序,这可以手动进行。
由于模拟的客户端对象应该可以在 config
模块模拟中访问,因此需要将变量提升到模块范围,这允许在组件模块中模拟 client
。
可以像这样对链进行全面测试:
// module scope
jest.mock('../../config', () => (
__esModule: true,
default: mockClient
);
const mockClient =
items: jest.fn(),
type: jest.fn(),
...
;
...
// describe scope
let clientCalls;
beforeEach(() =>
clientCalls = [];
mockClient.items.mockImplementation(function () clientCalls.push(1); return this );
mockClient.type.mockImplementation(function () clientCalls.push(2); return this );
...
);
...
// test scope
component.instance().fetchFooter();
expect(clientCalls).toEqual([1,2,3,4,5]);
expect(mockClient.items).toBeCalledWith();
expect(mockClient.type).toBeCalledWith("footer_link");
...
expect(mockClient.subscribe).toBeCalledWith(expect.any(Function));
let [handler] = mockClient.subscribe.mock.calls[0];
expect(component["setState"]).not.toBeCalled();
handler();
expect(component["setState"]).toBeCalledWith( resFooter:..., resFooterLinked:...);
【讨论】:
clientCalls 给出了一个空白数组。似乎 mockClient 变量没有被分配给对象。所以我尝试在顶部将 mockClient 定义为这个对象,但仍然给我错误 - Number of calls: 0.. 我错过了什么吗? 这取决于你如何模拟它,这在问题中没有显示。如果你不这样做,mockClient
将不会被神奇地使用而不是 client
。请显示client
在组件中的确切显示方式。如果已导入,则应在模块级别进行模拟。
哦.. 是的,在组件(.js)中,我已经从其他文件中导入了客户端,像这样 - import client from '../../config';
... 所以我需要对测试(test.js)做一些更改为此?
是的,你肯定会。还要确保在每次测试之前重置 Jest 间谍,请参阅 jestjs.io/docs/en/configuration#restoremocks-boolean
这有帮助.. 非常感谢!以上是关于用于链式函数调用的 Jest 单元测试的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 React、Jest 和 React-testing-library 为带有令牌的 api 调用编写单元测试?