概要
自分が業務でよく使う、テストのない.tsx
ファイルなどを処理を関数で切り出し、jestでリファクタリングする手順をメモします。
リファクタ前のコード
以下は日付のフォームとバリデーション処理です。
処理はtsxに書かれていてテストはありません。
日付バリデーションの条件は当日からプラス1日〜3日です。
本記事のコードはChatGPTに書いてもらいました
import React, { useState } from 'react'; const DateForm: React.FC = () => { const [selectedDate, setSelectedDate] = useState(''); const [error, setError] = useState(''); const validateDate = (date: string): boolean => { const inputDate = new Date(date); const today = new Date(); const tomorrow = new Date(today.getTime() + 86400000); // 1日後 const threeDaysLater = new Date(today.getTime() + 3 * 86400000); // 3日後 if (inputDate >= tomorrow && inputDate <= threeDaysLater) { setError(''); return true; } else { setError('Please select a date that is 1 to 3 days from today.'); return false; } }; const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { const newDate = event.target.value; if (validateDate(newDate)) { setSelectedDate(newDate); } }; return ( <div> <label htmlFor="date">Select a date: </label> <input type="date" id="date" value={selectedDate} onChange={handleChange} /> {error && <div style={{ color: 'red' }}>{error}</div>} </div> ); }; export default DateForm;
リファクタリング
バリデーション処理を切り出す
まずはテストしやすいようにバリデーションの処理を関数に切り出します。
tsxもバリデーション処理を切り出したことで見やすくなりました。
// validation.ts export const validateDate = (date: string): boolean => { const inputDate = new Date(date); const today = new Date(); const tomorrow = new Date(today.getTime() + 86400000); // 1日後 const threeDaysLater = new Date(today.getTime() + 3 * 86400000); // 3日後 return inputDate >= tomorrow && inputDate <= threeDaysLater; };
tsxファイル
import React, { useState } from 'react'; import { validateDate } from './validation'; // バリデーション関数をインポート const DateForm: React.FC = () => { const [selectedDate, setSelectedDate] = useState(''); const [error, setError] = useState(''); const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { const newDate = event.target.value; if (validateDate(newDate)) { setSelectedDate(newDate); setError(''); } else { setError('Please select a date that is 1 to 3 days from today.'); } }; return ( <div> <label htmlFor="date">Select a date: </label> <input type="date" id="date" value={selectedDate} onChange={handleChange} /> {error && <div style={{ color: 'red' }}>{error}</div>} </div> ); }; export default DateForm;
jestを書く
日付の計算で、過去の日付などの異常値テストや、境界値の正常テストを追加しています。
これで処理の切り出しとユニットテストの導入まで行えました。
条件判定系は複雑になりがちなので、ユニットテストで担保しておくと安心ですね。
import { validateDate } from './validation'; describe('Date Validation', () => { const formatDate = (date: Date) => date.toISOString().split('T')[0]; it('should accept a date exactly 1 day from today', () => { const tomorrow = new Date(); tomorrow.setDate(tomorrow.getDate() + 1); expect(validateDate(formatDate(tomorrow))).toBe(true); }); it('should accept a date exactly 3 days from today', () => { const threeDaysLater = new Date(); threeDaysLater.setDate(threeDaysLater.getDate() + 3); expect(validateDate(formatDate(threeDaysLater))).toBe(true); }); it('should reject a date today (boundary)', () => { const today = new Date(); expect(validateDate(formatDate(today))).toBe(false); }); it('should reject a date exactly 4 days from today (boundary)', () => { const fourDaysLater = new Date(); fourDaysLater.setDate(fourDaysLater.getDate() + 4); expect(validateDate(formatDate(fourDaysLater))).toBe(false); }); it('should reject a date in the past', () => { const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); expect(validateDate(formatDate(yesterday))).toBe(false); }); it('should reject a date far in the future', () => { const farFuture = new Date(); farFuture.setDate(farFuture.getDate() + 100); expect(validateDate(formatDate(farFuture))).toBe(false); }); it('should reject an invalid date format', () => { expect(validateDate('invalid-date')).toBe(false); }); });