らぼるてっく。

てっくてっく歩いてっく。

アプリケーション開発でGeneral logを用いた複雑なテストデータの作成(MySQL)

アプリケーション開発でGeneral logを用いた複雑なテストデータの作成(MySQL)

ラボルでバックエンド担当している川村です。

複雑なアプリケーション開発において以下をしたい時、事前にテストデータを準備しないといけない場面が多々あると思います。

  • 〇〇機能を試したい
  • 〇〇機能のテストしたい
  • 新規機能の開発したいが、その機能開発するまでに、前提となる状態を作りたい(会員登録済みアカウントの発行など)

複雑なプリケーションだとテストデータを作るだけでも、かなり骨が折れる作業となります。

簡単なアプリケーションであればテーブル構造を見るだけで作れそうではありますが、 複雑なアプリケーションではテーブル構造も複雑ですし、どのタイミングでどのデータが作成・更新・削除されるかコードを読まないと分かりません。

だからと言って、テーブル構造やコードを見ながら手動でテストデータを作成していては、必ずどこかでミスが発生します。 時間も浪費しますしね。

そこで、頑張って手動でデータを作るのではなく、DBの「general log」を使って簡単にテストデータを作成する方法を紹介します。

今回のテストデータ作成方法のざっくりとした解説

先に今回の方法をざっくり解説すると、

開発環境用のアプリケーションを自分で操作しながら、その過程で発行されたクエリを全部記録します(後述しますが、これがgeneral log)。 あとは、その記録したクエリを使いたい時に使うだけです。

※アプリケーションを操作しクエリを記録している最中に、DBクライアントツール等からInsert分等を発行すると、もちろんそれらも記録されてしまうので、ノイズにならないよう気をつけましょう

記録するときも、「〇〇機能のテストデータ」を作りたいとなったら、その機能を実際のアプリケーション通して、クエリを記録すればOKです。

例えば、Aという機能を使うためのテストデータを作りたい場合、事前に会員登録が必要だったとします(会員登録済みのアカウントデータ必要)

そのような場合は、以下の様にクエリを取得します。

  1. クエリの記録をONにする
  2. アプリケーション上で会員登録を行う
  3. 記録されたクエリを取得

会員登録済みのアカウントのデータが手に入ったので、該当のA機能を使う時に、DBを初期化して記録したクエリを流せばOKです。

このような感じで〇〇機能用のテストデータという形で、たくさんクエリを用意しておけば、様々な機能がすぐに使えるようになります。

また、これらのテストデータは実際のアプリケーションを通して作成しているので、データに直接人の手が入っておらず、不整合が発生しないことも保証できます(アプリケーションにバグがったら破綻しますが)

テーブル構造、コードなど全く意識せずにテストデータが作れて楽です。

general logを使ってテストデータを作成する

では具体的な作成方法について見ていきましょう。

今回はMySQLを前提に話しますが、PostgreSQLでもOracleでもその他のDBでも同じようなクエリが取得出来るはずです。

MySQLにおけるgeneral logを使うのですが、これは実行されたクエリをすべて記録するログになります。

なので、アプリケーションを通して操作したとしても、実行されたクエリが全て記録されます。 今回はそれを利用しようという話ですね。

general logが有効かどうか確認

general logが無効になっていることが主だと思うので、以下クエリでgeneral logが有効かどうか確認しましょう。

show variables like 'general_log%';

上記はOFFになっているので、有効にしましょう、

SET GLOBAL GENERAL_LOG = 'on';

以下のようにONになっていればOKです。これでクエリのログが取得できます。

general log確認する

以下クエリを実行して、テーブルにログを記録するようにしましょう。 ファイルにもログを記録出来ますが、DBのtable上にあったほうが操作しやすいので、tableを指定します。

SET GLOBAL log_output = 'TABLE';

mysql.general_logテーブルに発行したすべてのクエリが実行されるので、確認してみましょう。

SELECT
  *
FROM
  mysql.general_log;

以下のように出力されます。

カラムはいくつかありますが、今回のケースで必要となるのは、event_timeとargumentだけです。

  • event_time: いつクエリが実行されたか
  • argument: どういうクエリが実行されたか

ただ、このままだとselectのクエリも取得できてしまうので、取得クエリが大量になります。

insert/update/deleteを対象に検索してみましょう。

SELECT
  event_time,
  argument
FROM
  mysql.general_log
WHERE
  LOWER(CONVERT(argument USING utf8)) LIKE 'insert%'
  OR LOWER(CONVERT(argument USING utf8)) LIKE 'update%'
  OR LOWER(CONVERT(argument USING utf8)) LIKE 'delete%'; 

上記は少し複雑なクエリとなっていますが、これは検索時に大文字小文字を区別しないようにするためです。 argumentのdata_typeがmediumblobなので、大文字小文字を区別してしまうようなので、文字列に変換して検索するようにしています。

これでdeleteというクエリとDELETEというクエリ両方がヒットします。

クエリ実行したら以下のように、insert/update/deleteのクエリのみが取得できます。

アプリケーションを触りながらクエリを記録していく

特定の機能のデータを準備するために、そこまでに必要なデータを作るべくアプリケーションを操作していきましょう。

余分なクエリがgeneral logに保存されていたら、不整合が起こる可能性もあるので、truncateしておきましょう。

truncate mysql.general_log;

アプリケーションを操作しながら目的の機能を操作可能な状態まで到達できれば、general logを取得してどこかに保存しておきましょう。

このようにすることで、手動でデータを触らずに整合性が取れているテストデータを取得することが出来ます。

今回はアプリケーションを人が触りながら、実行クエリを取得する方法でしたが、 E2Eを組み合わせながら目的のテストデータを作るというのもいいかもしれません。

おわりに

ラボルでは、エンジニアを積極採用中です。1、2年目のエンジニアから経験豊富なテックリードやエンジニリングマネージャーまで、興味がある方はぜひご応募ください!!

labol.co.jp