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