I'm trying to use graphql-tools
's addMocksToSchema
function in a jest test with Apollo Client to allow me to test components that query Apollo by returning mock data that I can define for myself. However, the mock data isn't being properly returned when using useQuery
.
I have a UserButton
button that I'm trying to write tests for. The component is as follows:
// UserButton.jsx
import React from "react";
import PropTypes from "prop-types";
import { gql } from "@apollo/client";
import { useQuery } from "@apollo/client";
import Button from "./Button";
const GET_CURRENT_USER = gql`
query GetCurrentUser {
id
firstName
lastName
}
`;
function UserButton({ ...props }) {
const { data: userData } = useQuery(GET_CURRENT_USER);
return <Button {...props}>{userData.firstName} {userData.lastName}'s Button</Button>;
}
UserButton.propTypes = {
children: PropTypes.node,
};
export default UserButton;
Here is my test:
// UserButton.test.jsx
import React from "react";
import { render, screen } from "@testing-library/react";
import UserButton from "../UserButton";
import ApolloMockingProvider from "../../providers/ApolloMockingProvider";
import userEvent from "@testing-library/user-event";
import { debug } from "webpack";
describe("UserButton", () => {
let onClickMock;
beforeEach(() => {
jest.resetAllMocks();
onClickMock = jest.fn();
});
it("Renders with first and last name.", () => {
render(
<ApolloMockingProvider>
<UserButton />
</ApolloMockingProvider>
);
const button = screen.getByRole("button");
expect(button).toHaveTextContent("John Smith's Button")
});
});
The ApolloMockingProvider
is based off the following article and the code for it is as follows:
// ApolloMoockingProvider.jsx
import {
ApolloClient,
ApolloProvider,
InMemoryCache,
} from "@apollo/client";
import { SchemaLink } from "@apollo/client/link/schema";
import { addMocksToSchema } from "@graphql-tools/mock";
import { makeExecutableSchema } from "@graphql-tools/schema";
import React from "react";
const schemaString = `
type AvatarObjectType {
id: Int,
avatarUrl: String
}
scalar DateTime
scalar GenericScalar
type UserObjectType {
id: Int,
username: String,
firstName: String,
lastName: String,
avatar: AvatarObjectType,
dateCreated: DateTime,
notificationPreferences: GenericScalar,
}
type Query {
GetCurrentUser: UserObjectType,
}
`;
const globalMocks = {
String: () => "abc",
Int: () => 56,
UserObjectType: () => ({
firstName: "John",
lastName: "Smith",
}),
};
const ApolloMockingProvider = ({ children }) => {
const executableSchema = makeExecutableSchema({ typeDefs: schemaString });
const schemaWithMocks = addMocksToSchema({
schema: executableSchema,
mocks: globalMocks,
});
const client = new ApolloClient({
link: new SchemaLink({ schemaWithMocks }),
cache: new InMemoryCache(),
});
return <ApolloProvider client={client}>{children}</ApolloProvider>;
};
export default ApolloMockingProvider;
The ApolloMockingProvider
's globalMock
's object that's passed into schemaWithMocks
should return 'John Smith's Button' for the UserButton
's Renders with first and last name.
test. However, instead I get undefined for userData
as shown by this error message:
UserButton › Renders with first and last name.
TypeError: Cannot read property 'firstName' of undefined
16 | function UserButton({ ...props }) {
17 | const { data: userData } = useQuery(GET_CURRENT_USER);
> 18 | return <Button {...props}>{userData.firstName} {userData.lastName}'s Button</Button>;
| ^
19 | }
20 |
21 | UserButton.propTypes = {
at UserButton (js/components/button/UserButton.jsx:18:39)
Anybody know what the error here is? Reproduction repo is here with the failing test being located here:
EDIT:
I added loading
to the useQuery
in UserButton but still get the same undefined
error.
// UserButton.test.jsx
function UserButton({ ...props }) {
const { data: userData, loading } = useQuery(GET_CURRENT_USER);
let buttonText = "Loading...";
if (!loading) {
buttonText = `${userData.firstName} ${userData.lastName}'s Button`;
}
return <Button {...props}>{buttonText}</Button>;
}
And changed the UserButton.test.jsx
test accordingly:
it("Renders with first and last name.", async () => {
render(
<ApolloMockingProvider>
<UserButton />
</ApolloMockingProvider>
);
const button = screen.getByRole("button");
expect(button).toHaveTextContent("Loading...")
await waitFor(() => {
expect(screen.queryByText("John Smith's Button")).toBeInTheDocument();
});
});
But the error I get is still with userData
being undefined
UserButton › Renders with first and last name.
TypeError: Cannot read property 'firstName' of undefined
19 |
20 | if (!loading) {
> 21 | buttonText = `${userData.firstName} ${userData.lastName}'s Button`;
| ^
22 | }
23 | return <Button {...props}>{buttonText}</Button>;
24 | }