0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

next/navigationのhooksのモック

Posted at

これはなに

jestでのテスト時に、next/navigationのhooks (useRouter, usePathname, useSearchParams)のモックで困っている人が一定数いそうな気がしたので、解決方法を書いてみます。

結論

こんな感じでモックしたプロバイダーを作って

// app-router-context-provider-mock.tsx

import {
  AppRouterContext,
  AppRouterInstance,
} from 'next/dist/shared/lib/app-router-context.shared-runtime';
import React from 'react';

type AppRouterContextProviderMockProps = {
  router?: Partial<AppRouterInstance>;
  children: React.ReactNode;
};

export const AppRouterContextProviderMock = ({
  router,
  children,
}: AppRouterContextProviderMockProps): React.ReactNode => {
  const mockedRouter: AppRouterInstance = {
    back: jest.fn(),
    forward: jest.fn(),
    push: jest.fn(),
    replace: jest.fn(),
    refresh: jest.fn(),
    prefetch: jest.fn(),
    ...router,
  };
  return (
    <AppRouterContext.Provider value={mockedRouter}>
      {children}
    </AppRouterContext.Provider>
  );
};
// pathname-context-provider-mock.tsx

import { PathnameContext } from 'next/dist/shared/lib/hooks-client-context.shared-runtime';
import { ReactNode } from 'react';

type PathNameContextProviderMockProps = {
  pathname: string;
  children: ReactNode;
};

export const PathNameContextProviderMock = ({
  pathname,
  children,
}: PathNameContextProviderMockProps): ReactNode => {
  return (
    <PathnameContext.Provider value={pathname}>
      {children}
    </PathnameContext.Provider>
  );
};
// search-params-context-provider-mock.tsx

import { SearchParamsContext } from 'next/dist/shared/lib/hooks-client-context.shared-runtime';

type SearchParamsContextProviderMockProps = {
  children: React.ReactNode;
  searchParams: URLSearchParams;
};

export const SearchParamsContextProviderMock = ({
  searchParams,
  children,
}: SearchParamsContextProviderMockProps) => {
  return (
    <SearchParamsContext.Provider value={searchParams}>
      {children}
    </SearchParamsContext.Provider>
  );
};

テストで使えば良いです。

// hoge.spec.tsx

import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime';
import { AppRouterContextProviderMock } from './app-router-context-provider-mock';
import { PathNameContextProviderMock } from './pathname-context-provider-mock';
import { SearchParamsContextProviderMock } from './search-params-context-provider-mock';
import { render, screen } from '@testing-library/react';

type Props = {
  push: AppRouterInstance["push"];
  pathname: string;
  searchParams: URLSearchParams;
}

describe('なんかのテスト', () => {
  const WrappedElement = ({push, pathname, searchParams}: Props) => (
    <AppRouterContextProviderMock router={{push}}>
      <PathNameContextProviderMock pathname={pathname}>
        <SearchParamsContextProviderMock searchParams={searchParams}>
          <テストしたいコンポーネント />
        </SearchParamsContextProviderMock>
      </PathNameContextProviderMock>
    </AppRouterContextProviderMock>
  );

  it('should render correctly', () => {
    const push = jest.fn();
    const searchParams = new URLSearchParams();
    searchParams.set('p', 'test');
    render(<WrappedElement push={push} pathname='/hoge' searchParams={searchParams} />);
    // 以下省略
  });
});

なぜこれで動くの?

Next.jsのコードを読むと、それぞれコンテキストからこれらのデータを取ってきていることが分かります。

これら、使われているコンテキストのproviderでモックをセットしてあげることで、テスト実行時にモックが使われます。

参考

手前味噌ですが、こちらに書いた内容をちょっと見直したものです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?