LoginSignup
3
2

More than 1 year has passed since last update.

【Jest】globalオブジェクトを使ったコンポーネントのテストのやり方

Last updated at Posted at 2022-01-06

カスタムglobalオブジェクトを定義してそれを使うコンポーネントのテスト

例えばこんな感じでhogePropertyをglobalで定義した

index.html
<script>
  window.hogeProperty = {
    hogeProp: "hoge",
    hogeFn(arg1 = "", arg2 = "") {
      console.log(`${arg1}_${arg2}`)
    }
  };
</script>

globalオブジェクトを使うコンポーネント

useGlobalProperty.tsx
/**
 * hogeボタンをクリックすると`hogeProperty`の`hogeFn`を実行するコンポーネント 
 */
export default function UseGlobalProperty() {
  // globalオブジェクト呼ぶとtsエラー出るので制御
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore
  const { hogeProp, hogeFn } = hogeProperty;

  function clickHandler() {
    hogeFn(hogeProp, 'bar');
  }

  return (
    <>
      <button onClick={clickHandler} type='button'>
        {hogeProp}ボタン
      </button>
    </>
  );
}

globalオブジェクトを使うコンポーネントのテストコード

useGlobalProperty.spec.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import UseGlobalProperty from './useGlobalProperty';

describe('UseGlobalProperty', () => {
  test('hogeボタンクリック時、hogeFnが実行される', () => {
    // NOTE: テスト中のglobalプロパティを定義する
    this.global.hogeProperty = {
      hogeProp: 'hoge', // ←使わないので書かなくても良い
      hogeFn(arg1 = '', arg2 = '') {
        console.log(`${arg1}_${arg2}`); // ←使わないので関数の中身は書かなくても良い
      },
    };

    // NOTE: globalオブジェクト呼ぶとtsエラー出るので制御
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    const { hogeProperty } = global;
    const { hogeProp } = hogeProperty;

    // NOTE: hogeFnが呼ばれたかをテストする為にspyOnする
    const hogeFnSpy = jest.spyOn(hogeProperty, 'hogeFn');

    render(<UseGlobalProperty />);

    fireEvent.click(screen.getByText(`hogeボタン`));

    // 1回だけ呼ばれたかテスト
    expect(hogeFnSpy).toHaveBeenCalledTimes(1);
    // 引数が正しいかテスト
    expect(hogeFnSpy).toHaveBeenCalledWith('hoge', 'bar');
  });
});

document.cookieのテスト

document.cookieに値をセットするコンポーネント

useDocumentCookie.tsx
/**
 * setCookieボタンクリックするとcookieに値を設定するコンポーネント
 */
export default function UseDocumentCookie() {
  function setCookie(): void {
    // eslint-disable-next-line no-restricted-globals
    const { hostname } = location;
    document.cookie = `hogeKey=anyValue; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/; domain=${hostname}`;
  }

  return (
    <>
      <div>
        <button onClick={setCookie} type='button'>
          setCookieボタン
        </button>
      </div>
    </>
  );
}

document.cookieに値をセットするコンポーネントのテストコード

useDocumentCookie.spec.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import UseDocumentCookie from './useDocumentCookie';

describe('UseDocumentCookie', () => {
  test('setCookieボタンクリック時、cookieが設定される', () => {
    // NOTE: これがないとテスト実行でクッキー書き換えられない
    Object.defineProperty(this.global.document, 'cookie', {
      writable: true,
    });

    render(<UseDocumentCookie />);

    fireEvent.click(screen.getByText('setCookieボタン'));

    const { cookie } = document;
        // cookieの値が期待通り設定されたかテスト
    expect(cookie).toEqual(
      `hogeKey=anyValue; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/; domain=${hostname}`
    );

    // NOTE: このテストでcookieの値変更されて他のテストにも影響するので戻しておく
    Object.defineProperty(document, 'cookie', {
      value: undefined,
    });

    // NOTE: 一応戻す
    Object.defineProperty(this.global.document, 'cookie', {
      writable: false,
    });
  });
});

テスト時のglobalオブジェクトの定義はCustomEnvironmentにまとめる

globalプロパティの定義はCustomEnvironmentにまとめた方が可読性とか良さげ
またjest.config.jstestEnvironmentnode、 特定のテストだけjsdom環境にする必要がある時とかはCustomEnvironmentを作成する。

custom-js-dom-environment.ts
// eslint-disable-next-line import/no-extraneous-dependencies,import/no-import-module-exports
import JSDOMEnvironment from 'jest-environment-jsdom';

class CustomJSDOMEnvironment extends JSDOMEnvironment {
  async setup(): Promise<void> {
    await super.setup();

    this.global.hogeProperty = {
      hogeProp: 'hoge',
      hogeFn(arg1 = '', arg2 = '') {
        // eslint-disable-next-line no-console
        console.log(`${arg1}_${arg2}`);
      },
    };

    Object.defineProperty(this.global.document, 'cookie', {
      writable: true,
    });
  }

  async teardown(): Promise<void> {
    Object.defineProperty(this.global.document, 'cookie', {
      writable: false,
    });
    await super.teardown();
  }
}

module.exports = CustomJSDOMEnvironment;

CustomEnvironmentを使うテストコード

useGlobalProperty.spec.tsx
/**
 * @jest-environment ./src/test/custom-js-dom-environment.ts
 */
// ↑ファイルの先頭に記述。custom-js-dom-environment.tsがあるパスを書く

import { render, screen, fireEvent } from '@testing-library/react';
import UseGlobalProperty from './useGlobalProperty';

describe('UseGlobalProperty', () => {
  test('hogeボタンクリック時、hogeFnが実行される', () => {
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    const { hogeProperty } = global;
    const { hogeProp } = hogeProperty;

    const hogeFnSpy = jest.spyOn(hogeProperty, 'hogeFn');

    render(<UseGlobalProperty />);

    fireEvent.click(screen.getByText(`hogeボタン`));

    expect(hogeFnSpy).toHaveBeenCalledTimes(1);
    expect(hogeFnSpy).toHaveBeenCalledWith('hoge', 'bar');
  });
});
useDocumentCookie.spec.tsx
/**
 * @jest-environment ./src/test/custom-js-dom-environment.ts
 */

import { render, screen, fireEvent } from '@testing-library/react';
import UseDocumentCookie from './useDocumentCookie';

describe('UseDocumentCookie', () => {
  afterEach(() => {
    // 他のテストも考えるとafterEachに書くのがベター
    Object.defineProperty(document, 'cookie', {
      value: undefined,
    });
  });

  test('setCookieボタンクリック時、cookieが設定される', () => {
    render(<UseDocumentCookie />);

    fireEvent.click(screen.getByText('setCookieボタン'));

    const { cookie } = document;

    expect(cookie).toEqual(
      `hogeKey=anyValue; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/; domain=${hostname}`
    );
  });
});

全てのコード、バージョン情報等はこちら

https://github.com/yamatoto/jest-sample
https://github.com/yamatoto/jest-sample/tree/master/src/views/useGlobalProperty

3
2
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
3
2