0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

reactアプリでのテストコード作成の忘備録(コード記述)

Posted at

概要

jestを使って実際にReactのテストコードを作成した際に詰まった部分の忘備録
前回

目次

1. inputのvalueを空に変更する
2. alertの呼び出しを処理を検知する
3. fetchMockを使ってのAPI処理の記述時の注意点
4. Routerを使って複数画面の切り替えを実装している場合のテスト

1. inputのvalueを空に変更する

userEventを使ってinputのvalueを変更する際の記述でtypeはテキストの入力なので空文字を指定してもダメ
clearを使ってvalueを削除する

App.test.js
- userEvent.type(screen.getByLabelText('ユーザー名'), '');
+ userEvent.clear(screen.getByLabelText('ユーザー名'));

  const user_name = await screen.queryByLabelText('ユーザー名');
  expect(user_name.value).toBe('');

2. alertの呼び出しを処理を検知する

window.alertをモックにして呼び出しを検知する

App.test.js

beforeAll(() => {
  window.alert = jest.fn();
});

describe('テストスイート', () => {
  test('アラート確認テスト', async () => {

    userEvent.click(screen.getByRole('button', {name: 'アラート呼び出し'}));
    expect(window.alert).toHaveBeenCalledWith('アラートで呼び出される文字列');

  });
});

3. fetchMockを使ってのAPI処理の記述時の注意点

fechMockを使ったAPI呼び出し処理のテストにおいて同じ値で呼び出され、別の結果が返ってくる場合のテストケースを記述する際は「fetchMock.restore();」でfetchMockをリセットする必要がある

ただし下記記述の場合、ログイン成功テスト内でエラーになった場合fetchMock.restore()が実行されずに、ログイン失敗テストでfetchMockの呼び出しでエラーになってしまう
[ログイン成功テスト] => fetchMockの呼び出しで定義されたものが無い旨のエラー
[ログイン失敗テスト] => fetchMockにすでに定義されているものがある旨のエラー

App.test.js

describe('テストスイート', () => {
  test('ログイン成功テスト', async () => {

    /* 「/login?login_id=test&pass=pass」ではなく「/login?login_id=a&pass=a」で呼び出しがあったと仮定 */

    fetchMock
      .get('/login?login_id=test&pass=pass', {
        result: 1,
      });
    expect(await screen.findByText('メイン画面')).toBeInTheDocument();
+   fetchMock.restore();
  });
  test('ログイン失敗テスト', async () => {

  /* 「/login?login_id=test&pass=pass」で正常に呼び出されたと仮定 */

    fetchMock
      .get('/login?login_id=test&pass=pass', {
        result: 2,
      });
    expect(await screen.queryByText('メイン画面')).toBeNull();
  });
});

afterEachを使ってtest実行後に毎回fetchMock.restore()を実行するようにすれば以降のテストでエラーにならなくなる
※ただし処理後に毎回fetchMockがリセットされてしまう為fetchMockのエラーはログに上がらないので注意
[ログイン成功テスト] => expect().toBeInTheDocument();で「メイン画面」が見つからない旨のエラー
[ログイン失敗テスト] => テスト成功

App.test.js

+afterEach(() => {
+  fetchMock.restore();
+});

describe('テストスイート', () => {
  test('ログイン成功テスト', async () => {

    /* 「/login?login_id=test&pass=pass」ではなく「/login?login_id=a&pass=a」で呼び出しがあったと仮定 */

    fetchMock
      .get('/login?login_id=test&pass=pass', {
        result: 1,
      });
    expect(await screen.findByText('メイン画面')).toBeInTheDocument();
-   fetchMock.restore();
  });
  test('ログイン失敗テスト', async () => {

    /* 「/login?login_id=test&pass=pass」で正常に呼び出されたと仮定 */

    fetchMock
      .get('/login?login_id=test&pass=pass', {
        result: 2,
      });
    expect(await screen.queryByText('メイン画面')).toBeNull();
  });
});

4. Routerを使って複数画面の切り替えを実装している場合のテスト

下記の様な構成においてrouterで複数画面を実装している場合のテスト記述方法
※AppからMainへの画面遷移時にuserをstateに入れて渡している

index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import App from './App';
import Main from './Main';

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <Switch>
        <Route exact path="/" component={App} />
        <Route exact path="/Main" component={Main} />
      </Switch>
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);
App.js
import React,{ useState,useEffect,Component } from 'react';
import { useHistory,useLocation } from 'react-router-dom';

function App() {
  /* ログイン */
  const login = (login_id, pass) => {
    /* 処理略 */
    history.push({ pathname: '/Main', state: { user: data }});
  }

  return(
    /* 略 */
  );

}
export default App;
Main.js

import React,{ useState,useEffect,Component } from 'react';
import { useHistory,useLocation } from 'react-router-dom';

const Main = () => {

  /* ユーザーデータ */
  const [user, setUser] = React.useState({});
  const location = useLocation();

  useEffect(() =>{
    setUser(location.state.user)
  },[]);

  return (
    /* 略 */
  );
}
export default Main;

createMemoryHistory()を使ってhistoryを作成
データをhistoryにpushしておく事でMain内でuseLocation()を使ってデータを取得出来る

Main.test.js

import React,{ useState,useEffect,Component } from 'react';
import { useHistory,useLocation,Router } from 'react-router-dom';
import { render, screen, fireEvent, act, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Main from '../Main';
import fetchMock from 'fetch-mock';
import { createMemoryHistory } from 'history'

describe('テストスイート', () => {
  test('テストケース', async () => {
+    const history = createMemoryHistory();

    const testUserData = {
      user_id: 'test_user_id',
      login_id: 'test_login_id',
      user_name: 'test_user_name',
      pass: 'test_pass'
    };

+    /* useLocation()で使用するデータをhistoryにpushしておく */
+    history.push({ pathname: '/Main', state: { user: testUserData }});

    render(
+      <Router history={history}>
        <Main />
+      </Router>
    );
    /* テスト略 */
  });
});
0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?