enzyme
enzyme copied to clipboard
Apollo MockedProvider with Enzyme Shallow Rendering
After spending a couple of days and looking all over the internet I'm starting to doubt what I'm doing wrong 🙈
I'm trying to test a component that uses useQuery with Enzyme's Shallow rendering method: https://github.com/jvanbaarsen/apollo-enzyme-test/blob/master/src/App.js#L17.
Somehow, whatever I try I keep getting the error that a client is not available and that I need to wrap my component in a ApolloProvider. I tried solving this by using a MockedProvider: https://github.com/jvanbaarsen/apollo-enzyme-test/blob/master/src/App.test.js#L19.
But no matter what I try, it just doesn't work with Shallow. When I try to use Mount it works, but I'd like to test a component in isolation as much as possible.
Is this something I'm doing wrong? Or just not support by shallow rendering?
A full test repo can be found here: https://github.com/jvanbaarsen/apollo-enzyme-test.
I'm cross-posting this issue also on the apollo-client repo, since I'm not really sure if this is a Enzyme or Apollo question.
Use the wrappingComponent option to pass the Provider, instead of passing it directly inside shallow.
Same issue here - I am passing in wrappingComponent option, just like it's documented and just like @jvanbaarsen is passing it in as well - but with no luck.
Works with mount but fails with shallow
@vdineva would you mind opening up a PR with a failing test case?
@ljharb Would it be sufficient for you if I add a failing test case to my example repo?
@jvanbaarsen i mean, i'll take it if that's all you can offer :-D but the real thing i need is a test case in this repo, so i can debug against it.
@ljharb Allright, lets start with the test case in my repo, and I'll do my best to come up with a test case in this repo.
Here is the test code in my repo: https://github.com/jvanbaarsen/apollo-enzyme-test/commit/04c9be5cf4e021f93538b97702b448ef098c6ef7#diff-abcd7b0d7cb054dcd47fded339bbbf73R68
Thanks for your help 💛
@ljharb Ok, here is where my limited JS eco system knowledge comes into play haha. How would one go about creating such a test case? That would require me to add Apollo to the mix to create a test case that uses the Apollo hooks right? or is there an easier way?
+1
I'm having the same issue, passing in wrappingComponent option the MockedProvider imported from @apollo/react-testing and is not working because I'm getting could not find client error.
+1 I'm having the same issue, passing in
wrappingComponentoption theMockedProviderimported from@apollo/react-testingand is not working because I'm getting could not find client error.
Same for me. If I directly use:
const wrapper = shallow(
<MockedProvider mocks={[mocks]} addTypename={false}>
<MyComponent />
</MockedProvider>
)
I cannot get any child components to test. ( I am using Material UI Select and should get MenuItem and some texts... Since I have tried without queries )
But if I am using wrappingComponent it will give no client error.
My settings for wrappingComponent is like this:
function MyProvider(props) {
return (
<MockedProvider mocks={[mocks]} addTypename={false}>
{props.children}
</MockedProvider>
);
}
MyProvider.propTypes = {
children: PropTypes.node
};
const wrapper = shallow(
<MyComponent />,
{
wrappingComponent: MyProvider,
}
)
Thanks for any help!
Hi @flora8984461 , with shallow function I could get to wrap with MockedProvider.
But try with mount, there should work. I know that's a less efficient and performant way, but it should work with mount.
Good luck!
both shallow and mount is working for me, the only problem is the mock returned value is undefined but it maps my list with the same length as my mock result data.
const setup = (): ShallowWrapper =>
shallow(
<MockedProvider mocks={mocks} addTypename={false}>
<Apollo data-test-id="apollo-component" />
</MockedProvider>,
);
describe('<Apollo /> Component', () => {
let wrapper: ShallowWrapper;
beforeEach(() => {
wrapper = setup();
});
describe('Renders', () => {
it('should render without crashing', async () => {
await wait(wrapper);
const component = findByTestId(wrapper, 'apollo-component');
expect(component.length).toBe(1);
});
});
});
Its working for me now I just need to add fragment matcher to resolve undefined results. but here's how I initialize MockedProvider. (it also works with shallow)
render-apollo.utils.ts
import React from 'react';
import { mount, ReactWrapper } from 'enzyme';
import { MockedProvider } from '@apollo/client/testing';
import { MockedProviderProps } from '@apollo/client/utilities/testing/mocking/MockedProvider';
export const renderApollo = ({
children,
addTypename = false,
...props
}: MockedProviderProps): ReactWrapper =>
mount(
<MockedProvider addTypename={addTypename} {...props}>
{children}
</MockedProvider>,
);
mocked-apollo-props.type.ts
import { MockedProviderProps } from '@apollo/client/utilities/testing/mocking/MockedProvider';
export type MockedApolloProps = Omit<MockedProviderProps, 'children'>;
initialize MockedProvider
const mocks = [ { ... } ]
const setup = (props: MockedApolloProps = { mocks }): ReactWrapper =>
renderApollo({
children: <YourReactComponent />,
...props,
});
Its working for me now I just need to add fragment matcher to resolve undefined results. but here's how I initialize MockedProvider. (it also works with shallow)
render-apollo.utils.ts
import React from 'react'; import { mount, ReactWrapper } from 'enzyme'; import { MockedProvider } from '@apollo/client/testing'; import { MockedProviderProps } from '@apollo/client/utilities/testing/mocking/MockedProvider'; export const renderApollo = ({ children, addTypename = false, ...props }: MockedProviderProps): ReactWrapper => mount( <MockedProvider addTypename={addTypename} {...props}> {children} </MockedProvider>, );mocked-apollo-props.type.ts
import { MockedProviderProps } from '@apollo/client/utilities/testing/mocking/MockedProvider'; export type MockedApolloProps = Omit<MockedProviderProps, 'children'>;initialize MockedProvider
const mocks = [ { ... } ] const setup = (props: MockedApolloProps = { mocks }): ReactWrapper => renderApollo({ children: <YourReactComponent />, ...props, });
Thanks @ssengalanto and @MathiasMinacapilli . This might be a good way. But I have not tried with it since later we switch to put useQuery and useMutation to another component file and just pass the mock data and functions to test the required components.
Here's another (ugly) workaround:
const client = new ApolloClient({
cache: new InMemoryCache({ addTypename: false }),
link: new MockLink(mocks, false),
})
Object.defineProperty(React, Symbol.for('__APOLLO_CONTEXT__'), {
value: React.createContext<ApolloContextValue>({
client,
}),
enumerable: false,
configurable: true,
writable: false,
})
const component = shallow(<MyComponent />)
Here's another (ugly) workaround:
const client = new ApolloClient({ cache: new InMemoryCache({ addTypename: false }), link: new MockLink(mocks, false), }) Object.defineProperty(React, Symbol.for('__APOLLO_CONTEXT__'), { value: React.createContext<ApolloContextValue>({ client, }), enumerable: false, configurable: true, writable: false, }) const component = shallow(<MyComponent />)
Just tried that workaround, unfortunately, it does not work.
No updates on this issue?
There is my ugly workaround to the issue
jest.mock('@apollo/client', () => ({
...jest.requireActual('@apollo/client'),
useQuery: jest.fn(),
useMutation: jest.fn(),
}));
const { useQuery, useMutation } = require('@apollo/client');
export const renderWithApolloClient = (
Component
) => {
const wrapper = shallow(
<MockedProvider mocks={mocks} addTypename={false}>
{Component}
</MockedProvider>
);
const { client } = wrapper.props();
const { useQuery: useOrigQuery, useMutation: useOrigMutation } =
jest.requireActual('@apollo/client');
useQuery.mockImplementation((q, options) =>
useOrigQuery(q, { ...options, client })
);
useMutation.mockImplementation((m, options) =>
useOrigMutation(m, { ...options, client })
);
return wrapper.shallow();
}
In my case to get actual Component shallow wrapper instance I have to do .shallow() four times.
@masker if you have 4 HOCs, you're expected to do 4 .dive()s (.shallow() works also)