はじめに
かなり苦労しました。
問題
TypeError: Cannot read properties of null (reading 'length')のエラーが発生する。
エラーメッセージ
TypeError: Cannot read properties of null (reading 'length')
49 | // console.log("newRecordCount", newRecordCount);
50 |
> 51 | await expect(recordCount + 1).toBe(newRecords.length);
| ^
52 | });
53 |
at length (src/tests/componenteSample.spec.jsx:51:50)
at call (src/tests/componenteSample.spec.jsx:2:1)
at Generator.tryCatch (src/tests/componenteSample.spec.jsx:2:1)
at Generator._invoke [as next] (src/tests/componenteSample.spec.jsx:2:1)
at asyncGeneratorStep (src/tests/componenteSample.spec.jsx:2:1)
at asyncGeneratorStep (src/tests/componenteSample.spec.jsx:2:1)
解決方法
エラーのコード
const newRecords = await waitFor(() => {
const elements = screen.getAllByTestId('record');
if (elements.length > recordCount) {
return elements;
}
return null;
});
const newRecordCount = newRecords.length;
await expect(recordCount + 1).toBe(newRecords.length);
await
awaitは、async関数内でPromiseの結果(resolve、reject)が返されるまで待機する演算子。(処理を一時停止する)
Copilotによると、await expect(recordCount + 1).toBe(newRecordCount);は、expectの結果が返るまで待機するという意味にはならない。
expectは同期的に動作するため、awaitを付けても非同期処理として待機することはできない。
そのため、await expect(recordCount + 1).toBe(newRecords.length);の部分は、awaitを付けても newRecordsの結果を待つわけではない。
console.logで確認したところ、undefinedが返ってきていた。
awaitの場合、undefinedが返ってきたら再計算することはない。
waitFor
Copilotによると、expectを同期的に待機するためには、waitFor関数の中でexpectを使用する必要がある。
waitFor内でexpectを使用することで、条件が満たされるまで待機することができる。
Testing Libraryのドキュメントによると、waitForはタイムアウトに達するまで何度でもコールバックを実行する。デフォルトの間隔は50msで、デフォルトのタイムアウトは1000ms。
await waitFor(() => {
const newRecords = screen.getAllByTestId('record');
const newRecordCount = newRecords.length;
console.log("newRecordCount", newRecordCount);
expect(recordCount + 1).toBe(newRecordCount);
});
参考
async function内でPromiseの結果(resolve、reject)が返されるまで待機する(処理を一時停止する)演算子のこと。
https://qiita.com/soarflat/items/1a9613e023200bbebcb3#await%E3%81%A8%E3%81%AF
testing-libraryのドキュメント
https://testing-library.com/docs/dom-testing-library/api-async/#waitfor