Intended outcome:
To test a Next.js <Page /> component which is nested under an <ApolloProvider /> using a <MockedProvider />.
// tests/pages/index.test.tsx
import { render } from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
import IndexPage from 'src/pages/index';
describe('pages: /', () => {
it('renders without crashing', () => {
const { getByText } = render(
<MockedProvider>
<IndexPage characters={[{ id: '1', name: 'Rick' }]} />
</MockedProvider>,
);
expect(getByText('Rick')).toBeInTheDocument();
});
});
// src/pages/index.tsx
import React from 'react';
import { NextPage } from 'next';
import { ApolloPageContext } from 'next-with-apollo';
import { useLazyQuery } from '@apollo/client';
import CharactersQueries from 'src/lib/gql/Characters/Queries';
import { Character } from 'src/lib/gql/Characters/schema';
import { sendAppInsightsEvent } from 'src/lib/utils/Global';
import Card from 'src/components/Global/general/Card/Card';
import Button from 'src/components/Global/general/Button/Button';
export const htmlHeadTitle = 'Member App - Hub';
const getInitialProps = async (context: ApolloPageContext) => {
const { apolloClient } = context;
const { data } = await apolloClient.query({ query: CharactersQueries.list });
return { htmlHeadTitle, characters: data.characters.results };
};
interface PageProps {
characters: Character[];
}
const MemberAppHome: NextPage<PageProps> = ({ characters }) => {
return (
<>
<h1>This is the Member Hub index page.</h1>
<p>
<span role="img" aria-label="howdy!">
๐ค
</span>
{' '}
howdy!
</p>
<button type="button" onClick={() => sendAppInsightsEvent({ name: 'Test event' })}>
Send test event to app insights
</button>
<br />
<br />
{characters && (
// eslint-disable-next-line no-use-before-define
<div style={{ display: 'flex', flexWrap: 'wrap' }}>{characters.map(CharacterCard)}</div>
)}
</>
);
};
const CharacterCard = ({ id, name }: Character) => {
const [getImage, { loading, error, data }] = useLazyQuery(CharactersQueries.getImage, {
variables: { id },
});
return (
<Card key={id}>
{loading && (
<>
Loading image...
<br />
<progress />
</>
)}
{error && (
<pre>
Error:
{error.message}
</pre>
)}
{data && <img src={data.character.image} alt="character" />}
<h4>{name}</h4>
{!data && <Button onClick={(e) => getImage()}>Load Image</Button>}
</Card>
);
};
MemberAppHome.getInitialProps = getInitialProps;
export default MemberAppHome;
md5-b2e8a44085be58577be1b24707f54004
FAIL tests/pages/index.test.tsx
โ Test suite failed to run
TypeError: Object prototype may only be an Object or null: undefined
at setPrototypeOf (<anonymous>)
1 | import { render } from '@testing-library/react';
> 2 | import { MockedProvider } from '@apollo/client/testing';
| ^
3 | import IndexPage from 'src/pages/index';
4 |
5 | describe('pages: /', () => {
at Object.__extends (node_modules/tslib/tslib.js:69:9)
at node_modules/@apollo/client/utilities/testing/mocking/MockedProvider.js:8:5
at Object.<anonymous> (node_modules/@apollo/client/utilities/testing/mocking/MockedProvider.js:32:2)
at Object.<anonymous> (tests/pages/index.test.tsx:2:1)
md5-6b69b3de5d51625fe3b419aea2960096
"@apollo/client": "^3.2.5",
"@testing-library/dom": "^7.26.6",
"@testing-library/jest-dom": "^5.11.5",
"@testing-library/react": "^11.1.2",
"babel-jest": "^26.6.3",
"jest": "^26.6.3",
"next": "^10.0.1",
"ts-jest": "^26.4.4",
"webpack": "^4.44.1"
any update?
Very interested in this. We adopted ApolloClient under the documentation's promise that we'd be able to test our implementation.
@adam-zhu
I was unable to reproduce the error, following your instructions I got the error:
guilhermeramos in member-hub on ๎ main
โฏ npm i && npm run test tests/pages/index.test.tsx
npm ERR! code E401
npm ERR! Unable to authenticate, need: Bearer authorization_uri=https://login.windows.net/565966d7-2be2-47b1-8a7d-681ca1a8f925, Basic realm="https://pkgsprodcus1.pkgs.visualstudio.com/", TFS-Federated
npm ERR! A complete log of this run can be found in:
npm ERR! /Users/guilhermeramos/.npm/_logs/2020-11-21T03_08_16_514Z-debug.log
I'm also getting this error with @apollo/client v3.3.6. Haven't found a workaround yet ๐ข
After hours of bashing my head against the wall I finally figured out how to fix this. It has something to do with import aliases that are not prefixed. For example I had my directory structure setup like this:
.
โโโ components/
โ โโโ form/
โ โ โโโ FormInput.tsx
โ โโโ auth
โโโ helpers/
โ โโโ form
โ โโโ auth
โโโ hooks/
โ โโโ form
โ โโโ auth
โโโ tests/
โโโ components
โโโ helpers
โโโ hooks
And I would use import aliases like this: import FormInput from "components/form/FormInput"
I switched to this directory structure:
.
โโโ src/
โ โโโ components/
โ โ โโโ form/
โ โ โ โโโ FormInput.tsx
โ โ โโโ auth
โ โโโ helpers/
โ โ โโโ form
โ โ โโโ auth
โ โโโ hooks/
โ โโโ form
โ โโโ auth
โโโ tests/
โโโ components
โโโ helpers
โโโ hooks
And added an alias in tsconfig.json
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"tests/*": ["tests/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
and in jest.config.js
module.exports = {
preset: "ts-jest",
setupFilesAfterEnv: ["<rootDir>/tests/setup.ts"],
roots: ["<rootDir>"],
moduleFileExtensions: ["ts", "tsx", "js", "json", "jsx"],
testPathIgnorePatterns: ["<rootDir>[/\\\\](node_modules|.next)[/\\\\]"],
transformIgnorePatterns: ["[/\\\\]node_modules[/\\\\].+\\.(ts|tsx)$"],
transform: {
"^.+\\.(ts|tsx|js)$": "babel-jest",
"^.+\\.svg$": "<rootDir>/tests/__transforms__/icon-transform.js",
},
moduleNameMapper: {
"\\.(css|less|sass|scss)$": "identity-obj-proxy",
"\\.(gif|ttf|eot|png)$": "<rootDir>/tests/__mocks__/file-mock.js",
"^@/svgs/.+.svg$": "<rootDir>/tests/__mocks__/svgr-mock.js",
"^@/(.*)$": "<rootDir>/src/$1",
"^tests/(.*)$": "<rootDir>/tests/$1",
},
restoreMocks: true,
resetMocks: true,
};
So now my imports look like this: import FormInput from "@/components/form/FormInput"
@guiaramos Here is a super slimmed down version of the repo where I think you should be able to reproduce the error: https://github.com/tcannon91/apollo-mock-failure
I have taken over this issue from the OP. Tried to remove all extra dependencies on the repo but leave the ts config intact for debugging purposes. My team and I tried the aliasing strategy mentioned by @phegman but didn't have any luck. I also tried to remove any aliasing code just to remove that as a dependency -- we don't have to use the aliasing if that will unblock us :)
@guiaramos Here is a super slimmed down version of the repo where I think you should be able to reproduce the error: https://github.com/tcannon91/apollo-mock-failure
I have taken over this issue from the OP. Tried to remove all extra dependencies on the repo but leave the ts config intact for debugging purposes. My team and I tried the aliasing strategy mentioned by @phegman but didn't have any luck. I also tried to remove any aliasing code just to remove that as a dependency -- we don't have to use the aliasing if that will unblock us :)
@tcannon91 ohh nice, I was able to fix it on your repo by doing the following changes:
jest.config.js
- moduleDirectories: ['node_modules', './'],
+ moduleDirectories: ['node_modules', 'src'],
BUT, there is a problem on @apollo/cllient current versions as described here https://github.com/apollographql/apollo-client/issues/7348
SO, to get it working, you can pin the deps to "@apollo/client": "3.2.4"
and don't forget to rm -rf node_modules && rm -rf package-lock.json and npm i
it should work fine now...
Wow! Such a sneaky change! Thank you so much! And thanks for the link to the apollo issue!
I believe there is an undocumented backwards incompatibility between Apollo client v2 and v3 within webpack and/or jest config that will cause this, due to some unknown bundling issues that have arisen since the v3 upgrade. I have attempted to document what I could in #7464.
I was able to fix this issue by not running my source code through either webpack's resolve as well as not running it through jest's moduleDirectories. Specifically the error below (from the original comment) was fixed by modifying my jest config.
TypeError: Object prototype may only be an Object or null: undefined
To clarify, this was my configuration before:
// webpack.config.js
resolve: {
modules: ['node_modules', 'client'],
}
// jest.config.js
moduleDirectories: ['node_modules', 'client'],
And I had to refactor my application and imports to support this config
// webpack.config.js
resolve: {
modules: ['node_modules'],
}
// jest.config.js
moduleDirectories: ['node_modules'],
It is worth mentioning that, in my case, our tests, as well as our UI components, are contained within client/ (much to my chagrin).
After a ton of googling I realized that in my case, this issue was being caused by trying to include test files alongside pages defined in pages/.... I ultimately changed the implementation to keep components defined elsewhere and only include an index file at the end of each /pages "route" that imports/re-exports relevant components, as described here: https://github.com/vercel/next.js/issues/3728#issuecomment-363964953
Most helpful comment
@tcannon91 ohh nice, I was able to fix it on your repo by doing the following changes:
jest.config.js
BUT, there is a problem on @apollo/cllient current versions as described here https://github.com/apollographql/apollo-client/issues/7348
SO, to get it working, you can pin the deps to
"@apollo/client": "3.2.4"and don't forget to
rm -rf node_modules && rm -rf package-lock.jsonandnpm iit should work fine now...