LoginSignup
5
1

More than 1 year has passed since last update.

【React】Testing Libraryでポップアップの表示テスト

Last updated at Posted at 2021-06-05

Testing Library学習中ということで、いろんなテストケースを記事にまとめています。

今回はマウスオーバーでポップアップが表示されるケースについてまとめました。
テスト実装中に遭遇したWarningの解消にかなり手こずったので、同様の問題にぶつかっている方がいたら記載した解消法が参考になるかと思います。

実装

Terms and Conditionの部分をマウスオーバーしたときに、No ice cream will actually be deliveredというポップアップが表示されるケースを考えます。
スクリーンショット 2021-06-05 11.07.22.png

テストするファイルのコンポーネントSummaryFormの中身は以下のようになっています。
スタイルにはreact-bootstrapを使用しています。

SummaryForm.jsx
export default function SummaryForm() {
  const [tcChecked, setTcChecked] = useState(false);

  const popover = (
    <Popover id="termsandconditions-popover">
      <Popover.Content>No ice cream will actually be delivered</Popover.Content>
    </Popover>
  );

  const checkboxLabel = (
    <span>
      I agree to
      <OverlayTrigger placement="right" overlay={popover}>
        <span style={{ color: 'blue' }}> Terms and Conditions</span>
      </OverlayTrigger>
    </span>
  );

  return (
    <Form>
      <Form.Group controlId="terms-and-conditions">
        <Form.Check
          type="checkbox"
          checked={tcChecked}
          onChange={(e) => setTcChecked(e.target.checked)}
          label={checkboxLabel}
        />
      </Form.Group>
      <Button variant="primary" type="submit" disabled={!tcChecked}>
        Confirm order
      </Button>
    </Form>
  );
}

同じ階層にtestフォルダを作成し、その中にSummaryForm.test.jsxを作成します。

SummaryForm.test.jsx
test('popover responds to hover', async () => {
  // SummaryFormの仮想DOMにアクセス
  render(<SummaryForm />);

  // ポップアップが初期状態で隠れていれば成功
  const nullPopover = screen.queryByText(
    /no ice cream will actually be delivered/i
  );
  expect(nullPopover).not.toBeInTheDocument();

  // マウスオーバーを模擬
  const termsAndConditions = screen.getByText(/terms and conditions/i);
  userEvent.hover(termsAndConditions);

  // ポップアップが表示されていれば成功
  const popover = screen.getByText(/no ice cream will actually be delivered/i);
  expect(popover).toBeInTheDocument();

  // マウスオーバーの解除を模擬
  userEvent.unhover(termsAndConditions);
  await waitForElementToBeRemoved(() =>
    screen.queryByText(/no ice cream will actually be delivered/i)
  );
});

要素へのマウスオーバーのイベント模擬には、userEventを使います。
userEventを使うことで細かいブラウザ操作の模擬を行うことができるため、fireEventよりもテストでの使用に適しています。

また、テストファイル内ではqueryByText getByTextというクエリメソッドを使い分けています。
query getにあたる部分をコマンド、Textにあたる部分をクエリタイプと呼ぶのですが、それぞれの部分で以下のようなことを指定しています。

コマンド

  • get: DOMに要素があることを期待しており、要素がなければエラーを返す
  • query: DOMに要素がないことを期待しており、要素がなければnullを返す
  • find: 要素が非同期で現れることを期待しており、Promiseを返す

クエリタイプ

  • Role: すべての要素
  • AltText: imgやinputなどのText以外の要素
  • Text: divやspanなどのForm外の要素

クエリタイプの使い分けについては、公式Docsに記載されている優先度に従います。

最後に、await waitForElementToBeRemovedの記述についての補足です。
アサーションについてはexpect(popover).toBeInTheDocument()で終わっているのですが、userEvent.unhover(termsAndConditions)以降の記述がないと以下のWarningに遭遇します。

スクリーンショット 2021-06-05 9.48.36.png

このWarningはテスト内でDOMの更新を行った場合に発生します。
ただ、Warningに従ってact()でのラッピングを行う必要はなく(Testing Libraryですでに行っているため)、代わりにテスト終了後の状態を決めるような操作を行ってあげる必要があります。

userEvent.unhover(termsAndConditions)でマウスオーバーを解除したときのイベントを発生させ、await waitForElementToBeRemoved(...)で要素が消えた後のコールバックを非同期で実行しています。

参考資料

5
1
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
5
1