使用 MockedProvider 模拟 useQuery 返回的数据
Mocking data returned by useQuery using MockedProvider
我有一个简单的组件。它所做的只是使用 useQuery 获取数据并将其传递给另一个组件。该组件工作正常,但我无法在不添加此处所述的 hack 的情况下对其进行测试:
我看过 MockedProvider requires timeout,这是 2.5 年前提出的问题。还有其他方法吗?我无法相信图书馆团队提倡以 wait/setTimeout 为单位!
组件:
export const PROFILE_QUERY = gql`
query {
profile {
roles
}
}
`;
export const Connected = () => {
const { loading, data, error } = useQuery(PROFILE_QUERY);
const setCurrentProfile = (role: string) => {
cachedSettings(getSettings(role));
};
const roles = data?.profile?.roles;
return <Profile {...{ roles, loading, error, setCurrentProfile }} />;
};
测试用例:
import React from 'react';
import { render } from '@testing-library/react';
import { MemoryRouter } from 'react-router';
import Profile from '../Profile';
import ConnectedProfile from '..';
import { MockedProvider, MockedResponse } from '@apollo/client/testing';
import { PROFILE_QUERY } from '../Profile.connected';
jest.mock('../Profile', () => {
return jest.fn(() => null);
});
describe('Connected <Profile />', () => {
const renderComponent = (roles: string[] | undefined) => {
const mock: MockedResponse = {
request: {
query: PROFILE_QUERY
},
result: {
data: {
profile: {
roles
}
}
}
};
return render(
<MockedProvider mocks={[mock]} addTypename={false}>
<MemoryRouter>
<ConnectedProfile />
</MemoryRouter>
</MockedProvider>
);
};
it('multiple roles must have been passed', async () => {
renderComponent(['foo', 'bar']);
**//DOCUMENTATION says do following? - will have to wrap rendering in act as well
//await new Promise(resolve => setTimeout(resolve, 0));**
const args = (Profile as jest.Mock).mock.calls[0][0];
expect(args.roles).toEqual(['agent', 'administrator']);
});
it('no roles passed', async () => {
renderComponent(undefined);
//DOCUMENTATION says do following?
//await new Promise(resolve => setTimeout(resolve, 0));
const args = (Profile as jest.Mock).mock.calls[0][0];
expect(args.roles).toEqual(undefined);
});
afterEach(() => (Profile as jest.Mock).mockClear());
});
如果 MockedProvider
是异步的并且包含 one-tick 延迟,则要求至少延迟相同的时间量是合理的。这是异步测试的常见问题,也是流行的 flush-promises
实用程序所做的事情。
React 测试库专注于黑盒测试,并鼓励使用 waitFor
帮助程序而不依赖于实现。由于轮询间隔可能导致累积测试时间:
renderComponent(undefined);
await waitFor(() => {
expect(Profile).toBeCalledTimes(1);
expect(Profile).toBeCalledWith(expect.objectContaining({ roles: undefined }));
});
由于此 MockedProvider
行为已知并记录在案,因此可以在 renderComponent
助手本身中安全地实施它:
const renderComponent = async (roles: string[] | undefined) => {
...
const result = render(
<MockedProvider mocks={[mock]} addTypename={false}>...</MockedProvider>
);
await new Promise(resolve => setTimeout(resolve));
return result;
};
...
await renderComponent(undefined);
expect(Profile).toBeCalledTimes(1);
expect(Profile).toBeCalledWith(expect.objectContaining({ roles: undefined }));
我有一个简单的组件。它所做的只是使用 useQuery 获取数据并将其传递给另一个组件。该组件工作正常,但我无法在不添加此处所述的 hack 的情况下对其进行测试:
我看过 MockedProvider requires timeout,这是 2.5 年前提出的问题。还有其他方法吗?我无法相信图书馆团队提倡以 wait/setTimeout 为单位!
组件:
export const PROFILE_QUERY = gql`
query {
profile {
roles
}
}
`;
export const Connected = () => {
const { loading, data, error } = useQuery(PROFILE_QUERY);
const setCurrentProfile = (role: string) => {
cachedSettings(getSettings(role));
};
const roles = data?.profile?.roles;
return <Profile {...{ roles, loading, error, setCurrentProfile }} />;
};
测试用例:
import React from 'react';
import { render } from '@testing-library/react';
import { MemoryRouter } from 'react-router';
import Profile from '../Profile';
import ConnectedProfile from '..';
import { MockedProvider, MockedResponse } from '@apollo/client/testing';
import { PROFILE_QUERY } from '../Profile.connected';
jest.mock('../Profile', () => {
return jest.fn(() => null);
});
describe('Connected <Profile />', () => {
const renderComponent = (roles: string[] | undefined) => {
const mock: MockedResponse = {
request: {
query: PROFILE_QUERY
},
result: {
data: {
profile: {
roles
}
}
}
};
return render(
<MockedProvider mocks={[mock]} addTypename={false}>
<MemoryRouter>
<ConnectedProfile />
</MemoryRouter>
</MockedProvider>
);
};
it('multiple roles must have been passed', async () => {
renderComponent(['foo', 'bar']);
**//DOCUMENTATION says do following? - will have to wrap rendering in act as well
//await new Promise(resolve => setTimeout(resolve, 0));**
const args = (Profile as jest.Mock).mock.calls[0][0];
expect(args.roles).toEqual(['agent', 'administrator']);
});
it('no roles passed', async () => {
renderComponent(undefined);
//DOCUMENTATION says do following?
//await new Promise(resolve => setTimeout(resolve, 0));
const args = (Profile as jest.Mock).mock.calls[0][0];
expect(args.roles).toEqual(undefined);
});
afterEach(() => (Profile as jest.Mock).mockClear());
});
如果 MockedProvider
是异步的并且包含 one-tick 延迟,则要求至少延迟相同的时间量是合理的。这是异步测试的常见问题,也是流行的 flush-promises
实用程序所做的事情。
React 测试库专注于黑盒测试,并鼓励使用 waitFor
帮助程序而不依赖于实现。由于轮询间隔可能导致累积测试时间:
renderComponent(undefined);
await waitFor(() => {
expect(Profile).toBeCalledTimes(1);
expect(Profile).toBeCalledWith(expect.objectContaining({ roles: undefined }));
});
由于此 MockedProvider
行为已知并记录在案,因此可以在 renderComponent
助手本身中安全地实施它:
const renderComponent = async (roles: string[] | undefined) => {
...
const result = render(
<MockedProvider mocks={[mock]} addTypename={false}>...</MockedProvider>
);
await new Promise(resolve => setTimeout(resolve));
return result;
};
...
await renderComponent(undefined);
expect(Profile).toBeCalledTimes(1);
expect(Profile).toBeCalledWith(expect.objectContaining({ roles: undefined }));