この投稿では、テスト ライブラリを使用して、コンテキストに依存する React コンポーネントをテストするための思考プロセスを説明します。私の目的は、これらのコンポーネントをテストするための別のアプローチを検討し、モックを使用する場合とコンテキストをモックせずにテストする場合の長所と短所を検討することです。それぞれのアプローチがテストの信頼性にどのような影響を与えるかを見ていき、実際のアプリケーションにおいて、ある方法が他の方法よりも有益である場合とその理由についての洞察を共有します。
ReactJS コンテキストは、ReactJS コンポーネントの構造における一般的な問題、つまりプロップドリルの解決策として登場しました。プロップドリルは、同じデータセットにアクセスする必要がある一連のコンポーネントがある場合に発生します。コンテキスト メカニズムにより、コンテキスト自体が最初の子孫である限り、コンポーネントは同じデータ セットを共有できます。
reactjs ドキュメントでは、テーマを保持するためのコンテキストが使用されます。他のコンポーネントがこの情報を必要とする可能性があるため、ドキュメントでは props 経由で値を渡すのではなく、コンテキストを使用してその情報を処理します。もう 1 つの例は、アプリケーションのレイアウトを保持するためのコンテキストの使用です。json-tool の例では、App.tsx は、すべてのアプリケーションで使用できる DefaultLayout コンテキストでアプリケーションをラップします。
次の例では、テーマ アプリが使用されます。これは、ユーザーが明るいテーマと暗いテーマを切り替えることができるアプリケーションです。このアプリは、reactjs 公式ドキュメントでも使用されています。このアプリケーションは、ライト テーマ モードとダーク テーマ モードを切り替えるシンプルなトグルで構成されています。アプリケーションは非常にシンプルで、すべてを 1 つのファイルにプロットできます:
import { createContext, useContext, useState } from 'react' const ThemeContext = createContext('light') function Page() { const theme = useContext(ThemeContext) return ( <div> <p>current theme: {theme}</p> </div> ) } function App() { const [theme, setTheme] = useState('light') return ( <ThemeContext.Provider value={theme}> <button className={theme} onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')} > Toggle </button> <Page /> </ThemeContext.Provider> ) } export default App
このアプリケーションには、アプリとページという 2 つの主要コンポーネントがあります。 App コンポーネントはメイン コンポーネントとして機能し、現在のテーマの状態 (「明るい」または「暗い」のいずれか) が含まれます。また、テーマをライト モードとダーク モードの間で切り替えるボタンも含まれています。 Page コンポーネントは App の子であり、テーマ コンテキストを使用して現在のテーマを表示します。 App コンポーネントのボタンは単純なトグル ボタンで、クリックするとテーマが切り替わり、それに応じてコンテキスト値が更新されます。
次のセクションでは、テスト用のコンポーネントのスライスについて説明します。
通常、どのようなアプリケーションでも、どのような種類のテストを実行したいのか、どのスライスに取り組みたいのかに焦点を当てる必要があります。たとえば、アプリケーション全体ではなく、単一のコンポーネントをターゲットにすることができます。この例では、Page コンポーネントから始めます。これをテストするには、test-double を使用する必要があります。
テストダブルはコンテキストに依存するため、アプリ構造自体から取得され、それを変更するにはコンテキスト内の値も変更する必要があります。
reactjs のコンテキストを使用したテスト アプローチを開始するには、最初のテストの作成を開始します。
import { createContext, useContext, useState } from 'react' const ThemeContext = createContext('light') function Page() { const theme = useContext(ThemeContext) return ( <div> <p>current theme: {theme}</p> </div> ) } function App() { const [theme, setTheme] = useState('light') return ( <ThemeContext.Provider value={theme}> <button className={theme} onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')} > Toggle </button> <Page /> </ThemeContext.Provider> ) } export default App
ライト テーマが ThemeContext でデフォルトのテーマに設定されている場合、このテストは期待どおりに合格します。この最初の例も同様にテストすることもできますが、ダーク テーマに興味がある 2 番目のテストで物事が面白くなります。ダークテーマに入るには、reactjs コンテキストに依存していることを考慮して、テストダブルの使用を開始する必要があります。 2 番目のテストでは、vi.mocked だけでなく vi.mock もミックスに加えます。 2 番目に作成するテストでは、最初のテストも変更する必要があることに注意してください。
import { render, screen } from '@testing-library/react' import { Page } from './Page' describe('<Page />', () => { it('should render light as default theme', () => { render(<Page />) expect(screen.getByText('current theme: light')).toBeInTheDocument() }) })
両方のテスト ケースは、アプリケーションをテストするために偽物を使用しています。コンテキストから返されるデータを変更すると、テストも変更されます。ここでの注意点は次のとおりです。
このセクションで使用されている完成したコードは GitHub で入手できます
次のアプローチは、アプリケーションに埋め込まれたコンテキストを、分離したりテストダブルを使用したりせずに使用することです。 TDD でこのアプローチを採用すると、ユーザーがどのように動作するかをシミュレートする非常に単純なテストから始めることができます。
import { render, screen } from '@testing-library/react' import { Page } from './Page' import { useContext } from 'react' vi.mock('react', () => { return { ...vi.importActual('react'), useContext: vi.fn(), createContext: vi.fn() } }) describe('<Page />', () => { it('should render light as default theme', () => { vi.mocked(useContext).mockReturnValue('light') render(<Page />) expect(screen.getByText('current theme: light')).toBeInTheDocument() }) it('should render dark theme', () => { vi.mocked(useContext).mockReturnValue('dark') render(<Page />) expect(screen.getByText('current theme: dark')).toBeInTheDocument() }) })
次に 2 番目のテストに続いて、デフォルトでライト テーマを設定します。
import { render, screen } from '@testing-library/react' import App from './App' import userEvent from '@testing-library/user-event' describe('<App />', () => { it('should render toggle button', () => { render(<App />) expect(screen.getByText('Toggle')).toBeInTheDocument() }) })
そして最後にテーマの切り替え:
import { render, screen } from '@testing-library/react' import App from './App' import userEvent from '@testing-library/user-event' describe('<App />', () => { it('should render toggle button', () => { render(<App />) expect(screen.getByText('Toggle')).toBeInTheDocument() }) it('should render light as default theme', () => { render(<App />) expect(screen.getByText('current theme: light')).toBeInTheDocument() }) })
この戦略の注意点:
このセクションで使用されている完成したコードは GitHub で入手できます
このセクションでは、さまざまなプロパティに関する各アプローチの長所と短所を説明します。
コンテキストにテストダブルを使用すると、この種の変更に対してテストが脆弱になります。 props を使用して useContext の使用をリファクタリングすると、動作が失敗しない場合でもテストが自動的に失敗します。テストダブルを使用しないオプションを使用すると、その意味でのリファクタリングがサポートされます。
reactjs からのコンテキスト プロバイダーに直接依存するのではなく、カスタム コンテキストを使用しても同じことが起こります。テストダブルを使用せずにオプションを使用すると、リファクタリングが有効になります。
このガイドでは、テストダブルを必要とせずにコンテキストに依存するコンポーネントをテストする方法を検討し、テストをより単純にして実際のユーザーの操作に近づけ、各アプローチの長所と短所を対比させました。可能な限り、ユーザー インタラクションを反映した単純なアプローチに従う必要があります。ただし、テストダブルが必要な場合は、テストコードの保守性を考慮して使用する必要があります。簡単なテストを行うことで、本番コードで自信を持ってリファクタリングできるようになります。
これらの手順に従うことで、テスト スキルを継続的に向上させ、React アプリケーションがリファクタリングに対応できるようにすることができます。
以上がReactJS コンテキストのテスト - テストダブルのガイドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。