概要
jestを使って実際にReactのテストコードを作成した際に詰まった部分の忘備録
前回
目次
1. inputのvalueを空に変更する
2. alertの呼び出しを処理を検知する
3. fetchMockを使ってのAPI処理の記述時の注意点
4. Routerを使って複数画面の切り替えを実装している場合のテスト
1. inputのvalueを空に変更する
userEventを使ってinputのvalueを変更する際の記述でtypeはテキストの入力なので空文字を指定してもダメ
clearを使ってvalueを削除する
- userEvent.type(screen.getByLabelText('ユーザー名'), '');
+ userEvent.clear(screen.getByLabelText('ユーザー名'));
const user_name = await screen.queryByLabelText('ユーザー名');
expect(user_name.value).toBe('');
2. alertの呼び出しを処理を検知する
window.alertをモックにして呼び出しを検知する
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にすでに定義されているものがある旨のエラー
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();で「メイン画面」が見つからない旨のエラー
[ログイン失敗テスト] => テスト成功
+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に入れて渡している
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')
);
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;
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()を使ってデータを取得出来る
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>
);
/* テスト略 */
});
});