OpenWork Tech Blog

社員クチコミサービスを運営しているオープンワークエンジニアによるテックブログです。

自動テスト実行時に作られるデータは邪魔

こんにちは。Webアプリエンジニアの藤原です。 今回は自動テストで行った改善について紹介したいと思います。

課題

テストデータの消し忘れによる不整合データ

(テストデータの作り方に問題がある、という意見もありますが、それは一旦置いておきます。)

弊社の自動テスト、中でもいわゆるファンクショナルテストは開始時にFixtureクラスを使ってテストに必要な状態のオブジェクトを生成し、実際にデータベースにまで登録していました。

一応、テスト終了時にそれら作成したテストデータをDBから削除する、というところまでがルールなのですが、扱うデータが増えてくると偶に見落としが発生し、テストが終わってもデータベースに残り続けるという事象が発生していました。

これの何が問題かというと、テスト用に作成したデータで運用上ありえないパターンの可能性があることです。これにより、手動でWebページの動作確認をするときにエラーが発生してしまうことがありました。

解決方法

結論から言うと、以下のライブラリを導入したことにより解決できました。

https://github.com/dmaicher/doctrine-test-bundle

見つけた経緯としては、今回のような課題感を持ちつつ、手軽な解決方法が無いかなと都合の良いことを考えながらたまたまSymfony公式のドキュメントを読んでいたところ、以下のような記事を見つけました。

https://symfony.com/doc/5.4/the-fast-track/ja/17-tests.html#getesutono-hounidetabesuworisettosuru

どういう機能を提供してくれるのか

簡単に言うと、テスト開始時にトランザクションを開始し、テストが終わったらロールバックする、という機能が各自動テストに導入されます。

graph TB
    A-->B-->C-->D-->E
    A("1.トランザクション開始")
    B("2.テストデータ作成")
    C("3.テスト実施")
    D("4.テスト実施完了")
    E("5.ロールバック")

なぜこれでテストデータが残らないかというとコミットしていないからです。

トランザクション中にデータベースに対して行われた変更はコミットしなければ永続化されません。

「2.テストデータ作成」を行った後に「3.テスト実施」されますが、トランザクション中であればコミットされていなくても作成・更新されたテストデータの参照が可能なため無事に「4.テスト実施完了」します。 その後「5.ロールバック」することでトランザクション前の状態にデータベースが戻されるのでテストデータが残らない、という仕組みです。

想定以上のメリット

今回のdoctrine-test-bundleを導入したことでファンクショナルテストの実行時間が短くなりました。

考えてみれば当たり前で、doctrine-test-bundleを導入する前は特に工夫もなく、テストデータ1件作成ごとにデータベースに永続化されていました。つまり内部的にコミットがテストデータ分実行されていました。 doctrine-test-bundleを導入後は、コミットが実行されなくなったため、その分実行時間が短くなったということです。

以下表が弊社での効果になります。

※作成しているテストデータの量にもよるので、あくまで参考になります。

導入前 導入後
1回目 52分 38分
2回目 52分 41分

注意

E2Eテストは未対応

E2Eテストの場合、テストデータを作成する処理とテストが動作する処理でセッションが異なるため、せっかくテストデータを作っても、E2Eテストが動作する環境からはそのテストデータが参照できないため正常にテストが実行できなくなります。

これを解決するためにはE2Eテストのコードには以下を追加して、E2Eテストでのみdoctrine-test-bundleの動作を止めてください。 このようにすることで他のテストコードに影響なくE2Eテストも正常に実行されます。

class E2ETest extends TestCase
{
    public static function setUpBeforeClass(): void
    {
        // テストクラス実行前にdoctrine-test-bundleの動作を停止
        StaticDriver::setKeepStaticConnections(false);
    }

    public static function tearDownAfterClass(): void
    {
        // テストクラス実行完了後にdoctrine-test-bundleの動作を再開
        StaticDriver::setKeepStaticConnections(true);
    }
}

最後に

今回は自動テスト改善の1つを紹介しましたが、まだまだ改善の余地はあると思っています。 私ならもっと良いテストが作れるのに、と思う方がいたらオープンワークに応募いただけたらと思います!

www.openwork.co.jp