👻

Flaky testsを避ける(Draft)

2023/02/13に公開

前提

この記事の前提として、以下のようなアプリケーションを想定している。

  • 何かしらのDBを使ったバックエンドのアプリケーション
    • この記事はRDBMS前提で書いてます
  • アプリケーションのユニットテストではモックではなく本物のデータベースを使用
  • テストで使用するデータベースはアプリケーションのテスト全体で同じものを共有している(テストケースごとにデータベースを作成しない)

Flaky testsの原因

DBが絡むユニットテストでは、大別すると以下の2種類のテストに分かれる。

  1. (APIなどで)データの作成or更新or削除するロジックのテスト
  2. (バッチ処理などで)条件に合致した全てのデータを処理するロジックのテスト

全てのテストでデータベースを共有している場合、上記の2種類のテストを書くときにデータの競合について考慮しておくと良い。というのは、1.のテストにおいてはテストデータのIDをランダムにすることで他のテストとデータがバッティングしないようにすればよいが、2.のテストにおいてはテスト開始前に該当のテーブルのデータが全て削除されていることが望ましい。そうしないと他のテストで作成されたデータが混ざってしまい、テストが意図しない結果になりえる。

具体的には、バッチ処理であるテーブルのデータを一括で取得してそれを更新する場合、更新されたデータ件数が前処理で作成されたデータの件数と一致するようなチェックを書いたりする。そのため、他のテストで作成されたデータがあった場合、件数が一致せずテストが失敗することになる。このようなテストデータの競合が自分が今まで遭遇したFlaky testsの最大の原因である。これを避けるためには、テストを始める前に対象のテーブルがクリアされていることが望ましい。

一方で、テストを始める前に対象のテーブルのデータを空にしてしまうと、別の問題を引き起こしてしまう。テストを並列で実行している場合、テストAの実行中にテストBの前処理でデータを消してしまい、テストケースAが失敗する、というようなことである。これを防ぐには2.のタイプのテストは並列実行しないように制御した方が良い。もし並列で実行したい場合は、テストを実行する前に専用のデータベースを作成してそれを使う方法もある。

Discussion