apollo graphqlアプリのUIテスト
apollo graphqlを使ったUIテストのセットアップをしてみました。
備忘録で残します。
テストはreact-tesing-liblaryで行います。
以下、テスト対象のアプリです。
(以前、書いた記事にテスト対象のアプリの作成方法を記載してます。読まなくてもできるように進めたいと思います。)前提
まず、普通にreact-tesing-liblaryを使ってみます。
import { render } from '@testing-library/react';
import App from '../App';
describe('App', () => {
test('App test', async () => {
const { findByTestId } = render(<App />);
const targetTestId = await findByTestId('hoge');
expect(targetTestId).not.toBe(null);
});
});
// 省略
const App = () => {
// 省略(apollo/graphql使ってデータのフェッチなどしてます。)
return (
<div data-testid="hoge">
<ul>
{repositoriesInfo?.map((repository: any) => {
const { name } = repository.node;
return <li key={name}>{name}</li>;
})}
</ul>
{/* 省略 */}
</div>
);
};
export default App;
でテストを実行すると、以下のようなerrorが表示されます。
Error: Uncaught [Invariant Violation: Could not find "client" in the context or passed in as an option. Wrap the root component in an <ApolloProvider>, or pass an ApolloClient instance in via options.]
「Providerでラップするか、ApolloClientインスタンスを渡してください」といった旨の内容です。
なので以下、docの通りテストのrenderを<MockedProvider />
でラップします。
-
<MockedProvider />
でラップ
今回はまず<MockedProvider />
を使用しwrapper関数を作成しようと思います。
import { render } from '@testing-library/react';
import { MockedProvider, MockedResponse } from '@apollo/client/testing';
type Mocks = ReadonlyArray<MockedResponse>;
type WrapMockedProviderArg = { ui: React.ReactElement; mocks?: Mocks };
export const wrapMockedProvider = (
wrapMockedProviderArg: WrapMockedProviderArg
) => {
const { mocks, ui } = wrapMockedProviderArg;
return render(
<MockedProvider mocks={mocks} addTypename={false}>
{ui}
</MockedProvider>
);
};
wrapper関数の内容としては、引数を2つ受け取ります。
引数はテスト対象のコンポーネント、テスト対象のコンポーネントで使用するmockを定義してます。
-
App.test.tsx
でwrapper関数(wrapMockedProvider
)を使用する
import App from '../App';
import { wrapMockedProvider } from './wrapMockedProvider';
const testRender = () => wrapMockedProvider({ ui: <App /> });
describe('App', () => {
test('App test', async () => {
const { findByTestId } = testRender();
const targetTestId = await findByTestId('hoge');
expect(targetTestId).not.toBe(null);
});
});
ここでテストを実行すると、「Providerでラップするか、ApolloClientインスタンスを渡してください」といった旨のエラーはなくなります。
ですが、テスト自体は失敗します。
原因はwrapMockedProvider
の第2引数にmockを渡しておらず、
apolloでハンドリングしてるerror時の画面がレンダリングされるからです。(以下の部分)
const App = () => {
const { error, ... } = useRepositoriesQuery({...});
// 省略
if (error) return <p>Error :(</p>;
// 省略
export default App;
なのでdata-testId="hoge"
を取得できずに失敗します。
- 正常にUIのテストを行えるようにする
App.test.tsx
にmockを定義します。
import { RepositoriesDocument } from '../graphql/generate';
const mock = {
request: {
query: RepositoriesDocument,
variables: {
after: null,
before: null,
first: 5,
last: null,
query: 'apollo graphql test',
},
},
result: {
data: {
search: {
edges: [
{
cursor: 'Y3Vyc29yOjE=',
node: { name: 'apollo-storybook-decorator' },
},
],
pageInfo: {
endCursor: 'Y3Vyc29yOjU=',
hasNextPage: true,
hasPreviousPage: false,
startCursor: 'Y3Vyc29yOjE=',
},
repositoryCount: 411,
},
},
},
};
const testRender = () => wrapMockedProvider({ ui: <App />, mocks: [mock] });
行ったこととしては、App.tsx
でuseRepositoriesQuery
を使用してるので、useRepositoriesQuery
のvariables
とdata
を擬似的に定義したmockをwrapMockedProvider
に渡すことでデータフェッチ成功時のUIをテストできるようにしてます。
ここでテストを実行すると、無事テストが通ると思います。
こんな感じでapollo graphqlを使ったUIテストができます。
以下、テスト込みのリポジトリです。
おわり。
Discussion