Reactのテストコードの練習をchatGPT4に作らせた

コンポーネントの表示テスト

実装コード (App.js):

import React from "react";

function App() {
  return (
    <div>
      <h1>Hello, World!</h1>
    </div>
  );
}

export default App;

テストコード (App.test.js):

import React from "react";
import { render } from "@testing-library/react";
import App from "./App";

// このテストは、Appコンポーネントが正しく表示されることを確認します。
// `render`関数を使ってコンポーネントをレンダリングし、`getByText`を使って、
// 期待されるテキストが表示されているかどうかをチェックします。
test("renders App component", () => {
  const { getByText } = render(<App />);
  const linkElement = getByText(/Hello, World!/i);
  expect(linkElement).toBeInTheDocument();
});

コンポーネントのpropsテスト

実装コード (Greeting.js):

import React from "react";

function Greeting(props) {
  return <p>Hello, {props.name}!</p>;
}

export default Greeting;

テストコード (Greeting.test.js):

import React from "react";
import { render } from "@testing-library/react";
import Greeting from "./Greeting";

// このテストは、Greetingコンポーネントが正しく表示され、渡されたpropsを使用していることを確認します。
// `render`関数を使ってコンポーネントをレンダリングし、`getByText`を使って、期待されるテキストが表示されているかどうかをチェックします。
test("renders Greeting component with props", () => {
  const { getByText } = render(<Greeting name="John" />);
  const greetingElement = getByText(/Hello, John!/i);
  expect(greetingElement).toBeInTheDocument();
});

イベントハンドラのテスト

実装コード (Button.js):

import React from "react";

function Button(props) {
  return <button onClick={props.onClick}>Click me!</button>;
}

export default Button;

テストコード (Button.test.js):

import React from "react";
import { render, fireEvent } from "@testing-library/react";
import Button from "./Button";

// このテストは、ButtonコンポーネントのonClickイベントハンドラが正しく動作することを確認します。
// `fireEvent`を使って、クリックイベントをシミュレートします。
test("triggers onClick event handler when clicked", () => {
  const handleClick = jest.fn();
  const { getByText } = render(<Button onClick={handleClick} />);
  const buttonElement = getByText(/Click me!/i);

  fireEvent.click(buttonElement);
  expect(handleClick).toHaveBeenCalledTimes(1);
});

useStateフックのテスト

実装コード (Counter.js):

import React, { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

export default Counter;

テストコード (Counter.test.js):

import React from "react";
import { render, fireEvent } from "@testing-library/react";
import Counter from "./Counter";

// このテストは、CounterコンポーネントのuseStateフックが正しく動作することを確認します。
// `fireEvent`を使って、クリックイベントをシミュレートし、カウントが増加することをチェックします。
test("increments count when button is clicked", () => {
  const { getByText } = render(<Counter />);
  const buttonElement = getByText(/Increment/i);

  fireEvent.click(buttonElement);
  expect(getByText(/Count: 1/i)).toBeInTheDocument();
});

useEffectフックのテスト

実装コード (Title.js):

import React, { useEffect } from "react";

function Title() {
  useEffect(() => {
    document.title = "React Testing";

    return () => {
      document.title = "React App";
    };
  }, []);

  return <div>Testing useEffect</div>;
}

export default Title;

テストコード (Title.test.js):

import React from "react";
import { render, cleanup } from "@testing-library/react";
import Title from "./Title";

// このテストは、TitleコンポーネントのuseEffectフックが正しく動作することを確認します。
// テスト後にcleanup関数を実行して、コンポーネントをアンマウントします。
afterEach(cleanup);

test("updates document title on mount and unmount", () => {
  render(<Title />);
  expect(document.title).toBe("React Testing");

  cleanup();
  expect(document.title).toBe("React App");
});

useContextフックのテスト

実装コード (ThemeContext.js):

import React, { createContext, useContext } from "react";

const ThemeContext = createContext("light");

export function useTheme() {
  return useContext(ThemeContext);
}

export function ThemeProvider({ children }) {
  return (
    <ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>
  );
}

実装コード (ThemedComponent.js):

import React from "react";
import { useTheme } from "./ThemeContext";

function ThemedComponent() {
  const theme = useTheme();

  return <div>Current theme: {theme}</div>;
}

export default ThemedComponent;

テストコード (ThemedComponent.test.js):

import React from "react";
import { render } from "@testing-library/react";
import { ThemeProvider } from "./ThemeContext";
import ThemedComponent from "./ThemedComponent";

// このテストは、ThemedComponentがuseContextフックを使って正しくテーマを取得できることを確認します。
// ThemeProviderコンポーネントを使って、テスト用のコンテキストプロバイダを作成します。
test("renders with correct theme from context", () => {
  const { getByText } = render(
    <ThemeProvider>
      <ThemedComponent />
    </ThemeProvider>
  );

  expect(getByText(/Current theme: dark/i)).toBeInTheDocument();
});

React.memoをテスト

実装コード (ExpensiveComponent.js):

import React from "react";

function ExpensiveComponent({ value }) {
  console.log("Rendering ExpensiveComponent...");
  return <div>Value: {value}</div>;
}

export default React.memo(ExpensiveComponent);

実装コード (ParentComponent.js):

import React, { useState } from "react";
import ExpensiveComponent from "./ExpensiveComponent";

function ParentComponent() {
  const [value, setValue] = useState(0);
  const [otherValue, setOtherValue] = useState(0);

  return (
    <div>
      <ExpensiveComponent value={value} />
      <button onClick={() => setValue(value + 1)}>Update value</button>
      <button onClick={() => setOtherValue(otherValue + 1)}>
        Update other value
      </button>
    </div>
  );
}

export default ParentComponent;

テストコード (ExpensiveComponent.test.js):

import React from "react";
import { render, fireEvent } from "@testing-library/react";
import ParentComponent from "./ParentComponent";

// このテストは、ExpensiveComponentがReact.memoによって正しくメモ化されていることを確認します。
// コンソールログによるレンダリングの追跡を行い、他の値が更新された場合にExpensiveComponentが再レンダリングされないことを確認します。
test("ExpensiveComponent is not re-rendered when otherValue changes", () => {
  const { getByText } = render(<ParentComponent />);
  const updateValueButton = getByText(/Update value/i);
  const updateOtherValueButton = getByText(/Update other value/i);

  const logSpy = jest.spyOn(console, "log").mockImplementation();

  fireEvent.click(updateOtherValueButton);
  expect(logSpy).not.toHaveBeenCalledWith("Rendering ExpensiveComponent...");

  fireEvent.click(updateValueButton);
  expect(logSpy).toHaveBeenCalledWith("Rendering ExpensiveComponent...");
});

カスタムフックをテスト

実装コード (useCounter.js):

import { useState } from "react";

function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue);

  function increment() {
    setCount(count + 1);
  }

  return { count, increment };
}

export default useCounter;

実装コード (CounterWithCustomHook.js):

import React from "react";
import useCounter from "./useCounter";

function CounterWithCustomHook() {
  const { count, increment } = useCounter(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

export default CounterWithCustomHook;

テストコード (useCounter.test.js):

import { renderHook, act } from "@testing-library/react-hooks";
import useCounter from "./useCounter";

// このテストは、useCounterカスタムフックが正しく機能することを確認します。
// `renderHook`と`act`を使って、フックの内部状態を検証します。
test("useCounter works correctly", () => {
  const { result } = renderHook(() => useCounter(0));

  expect(result.current.count).toBe(0);

  act(() => {
    result.current.increment();
  });

  expect(result.current.count).toBe(1);
});

すげーなと。

この記事が気に入ったらサポートをしてみませんか?