コンパイラかく語りき

import { Fun } from 'programming'

React testing library のテスト環境にて、tsyringe による依存注入を有効にする

tsyringe による依存注入

tsyringe を使うと TypeScript アプリケーションにて依存注入を実現することができます。

GitHub - microsoft/tsyringe: Lightweight dependency injection container for JavaScript/TypeScript

アプリケーションコードは以下のような感じ。(とてもシンプルな例)

// クラス定義側
import { injectable } from "tsyringe";

@injectable()
export class Foo {
  constructor(private storage: MyStorage) {}

  getItem = () => {
    return this.storage.get('foo')
  }
}


// クラス利用側
import "reflect-metadata"; // 依存解決に必要
import { container } from "tsyringe";
import { Foo } from "./foo";

const instance = container.resolve(Foo);

React 環境への組み込み

React 環境への組み込み方は、いくつかの方法があるかもしれませんが、Context 経由で渡す場合は以下のようになります。

// Context提供側
import 'reflect-metadata';
import { createContext } from 'react';
import { DependencyContainer, container } from 'tsyringe';
import { Foo } from "./foo";

container.register("Foo", { useClass: Foo }) // Fooクラスを "Foo" というトークンで登録

export const diContainerContext = createContext<DependencyContainer>(container);

export const ContextProvider = ({ children }) => {
  return (
    <diContainerContext.Provider value={container}>
      {children}
    </diContainerContext.Provider>
  )
}

export { container };

// Context利用側(useFoo.test.ts)
export const useFoo = () => {
  const diContainer = useContext(diContainerContext)
  const foo = diContainer.resolve('Foo')

  // 依存したクラスのメソッドを利用
  const item = foo.getItem()

  return { item }
}

もちろん、hooks ではなくコンポーネントからも利用可能です。

テスト環境のための tsyringe 設定

useFoo に対するユニットテストを作成しようとすると、依存性が解決できない、のようなエラーが出ます。

まず、テスト環境でも reflect-metadata を読み込めるようにします。

Jest や vitest なら setupTests.ts のような、ユニットテスト環境をセットアップするためのファイルを作成するかと思います。ここで reflect-metadata を読み込みます。

import 'reflect-metadata';

// 他には、以下のような初期化処理があるかもしれません。
import '@testing-library/jest-dom';

次に、テストファイルで Context を参照できるようにします。

これは、 @testing-library/react の renderHook のオプションを利用すれば OK です。第二引数に wrapper というキー名で、ContextProvider コンポーネントを渡します。

import { renderHook, waitFor } from '@testing-library/react';
import { ConfigProvider } from 'ConfigProvider';
import { useFoo } from './useFoo';

describe('useFoo', () => {
  it('itemを返す, async () => {
      renderHook(() => useFoo(), { wrapper: ConfigProvider });
    });

以上で、React テスト環境でも tsyringe を使った依存注入が動くようになると思います。

おまけ

ちなみに、tsyringe を使ってモック登録する例が以下の通り。便利ですね。

import { renderHook, waitFor } from '@testing-library/react';
import { ConfigProvider, container } from 'ConfigProvider';
import { useFoo } from './useFoo';

describe('useFoo', () => {
  it('itemを返す, async () => {
      // モック関数を登録
      container.register('Foo', { useValue: { getItem: () => 'aaa' } }); // 同じ container インスタンスを利用する点に注意
      const { item } = renderHook(() => useFoo(), { wrapper: ConfigProvider });
      expect(item).toBe('aaa')
    });