テストにおけるrace conditionとは?解説
race conditionは、結果が処理の相対的なタイミングや順序に依存する欠陥で、速度、スケジューリング、並列度によって結果が変わります。
race conditionはflakyなテストの主要な原因です。バグは処理が特定の順序でインターリーブしたときにしか現れないため、遅いrunnerで、重い並列度のもとで、あるいは一見ランダムに、断続的に表面化し、再現するのが非常に厄介になります。
race conditionとは何か
race conditionは、2つ以上の処理が異なる順序で完了しうり、少なくとも1つの順序が誤った結果を生むときに存在します。「レース」はそれらの間で起こり、勝った方が結果を変えます。正しいコードは、どちらが先に終わるかに依存してはいけません。
なぜflakinessを引き起こすのか
テストはしばしば、結果をassertする前に処理が終わっていると仮定します。処理が非同期の場合、速いマシンは間に合わせられても、遅いマシンや負荷の高いマシンでは間に合わないため、同じテストがローカルでは通りCIで失敗します。結果はタイミングに依存し、それがまさにflakyの定義です。
テストのどこに現れるか
- 非同期処理、タイマー、promiseが解決する前にassertすること。
- 分離なしに並列テストやworkerによって変更される共有状態。
- 並行して到着するアイテムの順序に依存すること。
- 実際の条件を待つ代わりに固定のsleepに頼ること。
どう修正するか
固定のsleepを、実際の条件への明示的な待機に置き換えます。テストが干渉できないよう共有状態を分離します。言語がrace detectorを提供しているなら使います。目標は、テストを決定論的にして、その結果がもはやレースの勝者に依存しないようにすることです。
Latchkeyの視点
race conditionはあなたのテストやコードの本物のバグなので、Latchkeyがあなたの代わりに修正することはできません。しかしその周りの一時的なインフラの失敗を自己修復することで、Latchkeyはノイズを減らし、残る失敗が、修正する価値のある本物のrace conditionであることをより明確にします。
重要なポイント
- race conditionは、結果をタイミングや順序に依存させます。
- ローカルでは通り、CIで失敗するflakyなテストの主要な原因です。
- 非同期のassertion、共有状態、固定のsleepがよくある原因です。
- 実際の条件を待ち、共有状態を分離することで修正します。