🎋

next/headを使ったmetadataを React Testing Library でテストする

2021/03/18に公開

Next.jsでmetaタグに仕込んだテキストをテストする時にちょっと詰まったのでメモ。

要約

next/head をモック化すればOK。

jest.mock('next/head', () => {
  return {
    __esModule: true,
    default: ({ children }: { children: Array<React.ReactElement> }) => {
      return <>{children}</>;
    },
  };
});

参考

How to test metadata using jest and react library test #11060
https://github.com/vercel/next.js/discussions/11060#discussioncomment-33628

サンプルコード(NG)

Head.jsx

import React from 'react'
import NextHead from 'next/head'

export const Head = ({
  return (
    <NextHead>
      <title>
        Hello
      </title>
      <meta
        content="TestContext"
        property="og:title"
      />
    </NextHead>
  )
}

Head.test.jsx

describe('Head のテスト', () => {
  test('titleがHelloであること', () => {
      render(<Head />, {
        container: document.head
      });
    expect(document.title).toBe('Hello')
  })
  test('metaタグのog:titleがTestContextであること', () => {
      render(<Head />, {
        container: document.head
      });
    expect(document.querySelector("meta[property='og:title']")
      ?.attributes.getNamedItem('content')?.value
    ).toBe('TestContext')
  })
})

テスト結果

Head のテスト › titleがHelloであること
expect(received).toBe(expected) // Object.is equality
Expected: "Hello"
Received: ""

Head のテスト › metaタグのog:titleがTestContextであること
expect(received).toBe(expected) // Object.is equality
Expected: "TestContext"
Received: undefined

デバッグしてみる

Head.test.jsx

const { debug } = render(<Head />, {
  container: document.head,
});
debug()

結果

  console.log
    <body />

bodyタグしか表示されていない。

補足

以下でもデバッグできる。

import { render, screen } from '@testing-library/react'

render(<Head />, {
  container: document.head,
});
screen.debug()

問題点

React Testing Library で Headコンポーネント をrenderしても body タグしか表示されない。

解決策

冒頭で書いた通り、next/head をモック化すればOK。

サンプルコード(OK)

Head.test.jsx

// 以下を追加
jest.mock('next/head', () => {
  return {
    __esModule: true,
    default: ({ children }: { children: Array<React.ReactElement> }) => {
      return <>{children}</>;
    },
  };
});

// 以下は変更なし
describe('Head のテスト', () => {
  test('titleがHelloであること', () => {
      render(<Head />, {
        container: document.head
    });
    expect(document.title).toBe('Hello')
  })
  test('metaタグのog:titleがTestContextであること', () => {
      render(<Head />, {
        container: document.head
      });
    expect(document.querySelector("meta[property='og:title']")
      ?.attributes.getNamedItem('content')?.value
    ).toBe('TestContext')
  })
})

これで正しくテストを実施することができるようになりました!
モック化の有無でテストの結果を確認してみてください。

Discussion