長年管理しているお客さんのプロジェクトのユニットテスの数が増えてきました。Featureテストも含めてテストケースの数は2,400以上あります。私の古めのWindowsのマシン(i7-9700CPU @ 3.00GHz)のWSL2環境では、7分51秒かかります。今回はこのテスト実行所有時間をほぼ1/1050秒以下に縮めた話です。

mysqlをメモリー内で実行

DBを使用するテストが多いので最初に考えたのは、使用しているmysqlサーバーのエンジンをInnoDBからMemoryとすることです。しかしこれはすぐに不可能とわかりました。MemoryのエンジンではTEXTのデータタイプを対応していないからです。

どうしたらよいかといろいろ調査すると、mysqlのDBを保存しているボリュームをハードディスクのストレージからtmpfsに変えれば良いらしい。tmpfsは以下に説明されるようにメモリに存在するファイルシステムです。
tmpfsとは

しかし、ローカル環境(WSL2)で実行されているmysqlサーバーをtmpfsで走らせるとなると、mysqlサーバーがいろいろなプロジェクトで共有されているためにとても不都合であるし、マシンを落としたらそれでDB内のデータは消えてしまうとなるともっと困ります。

ということで、sailを使用してdocker内でmysqlサーバーをtmpfsで実行するのが理想となりました。

sailのセットアップに関してはここでは説明しませんが、セットアップの産物としてdocker-compose.ymlが作成されます。

このdocker-compose.ymlのmysqlサーバーの定義において、以下のようにvolumesのsail-mysqlをコメントアウトして、tmpfsを追加します。

...
    mysql:
        image: 'mysql/mysql-server:8.0'
        ports:
            - '${FORWARD_DB_PORT:-3306}:3306'
        environment:
            MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ROOT_HOST: '%'
            MYSQL_DATABASE: '${DB_DATABASE}'
            MYSQL_USER: '${DB_USERNAME}'
            MYSQL_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ALLOW_EMPTY_PASSWORD: 1
        volumes:
            # - 'sail-mysql:/var/lib/mysql' # コメントアウト!
            - './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh'
        tmpfs: # ここを追加!
            - /var/lib/mysql
        networks:
            - sail
        healthcheck:
            test:
                - CMD
                - mysqladmin
                - ping
                - '-p${DB_PASSWORD}'
            retries: 3
            timeout: 5s
...

以下を実行して、

$ sail up

dockerを立ち上げて、docker内のmysqlサーバーのファイルシステムを見ると、

sh-4.4# df -h
Filesystem      Size  Used Avail Use% Mounted on
overlay         251G   26G  213G  11% /
tmpfs            64M     0   64M   0% /dev
tmpfs           7.8G     0  7.8G   0% /sys/fs/cgroup
shm              64M     0   64M   0% /dev/shm
/dev/sdd        251G   25G  214G  11% /docker-entrypoint-initdb.d/10-create-testing-database.sh
/dev/sde        251G   26G  213G  11% /etc/hosts
tmpfs           7.8G  199M  7.6G   3% /var/lib/mysql
tmpfs           7.8G     0  7.8G   0% /proc/acpi
tmpfs           7.8G     0  7.8G   0% /sys/firmware

/var/lib/mysqlがtmpfsとなっていますね。

テストを実行してみましょう。

$ sail artisan test

テスト実行の所有時間は、7分51秒から219秒から3分39秒と一気に半分以下となりました。

並行処理でテストを実行

今度は、パラレルテストを利用してもっと高速化してみましょう。

まず、必要なライブラリをインストールします。

$ sail composer require brianium/paratest --dev

その後、パラレルテストを実行します。

$ sail artisan test --parallel

tmpfsとパラレルを組み合わせたテスト実行となり、その結果、所有時間は驚異の49秒となりました。ほぼ1/10の時間です。

Docker内のmysqlサーバーでデータベースを閲覧すると、以下のように、testingのデータベース7個新たに作成sれて、計8個となっています。これは私のマシンが8コアのためです。パラレルテストは使用のマシンのコア数でデータベースの数が決まります。

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| laravel            |
| mysql              |
| performance_schema |
| sys                |
| testing            |
| testing_test_1     |
| testing_test_2     |
| testing_test_3     |
| testing_test_4     |
| testing_test_5     |
| testing_test_6     |
| testing_test_7     |
+--------------------+

ちなみに、tmpfsなしのパラレルテストの所要時間は、5分28秒でした。tmpfs + parallelが最速のチームということですね。

メルマガ購読の申し込みはこちらから。

By khino