mixiエンジニアがおくるソーシャルアプリ開発実践講座

第3回 自動テストと継続的インテグレーションを既存プロジェクトへ導入しよう

この記事を読むのに必要な時間:およそ 3 分

はじめに

はじめまして。(株)ミクシィの加藤和良です。2008年度に入社し,2011年1月からはシステム本部 技術部に所属しています。技術部は,日記やコミュニティといった特定のサービスに紐づかない,mixi全体を裏から支える部署です。「支える」ための方法は,実際のサービスの一部として動作する共通基盤から,開発効率を上げるために社内で動作しているものまで,多岐にわたります。

mixiでは,ここ数年で自動テストの導入が急速に進みました。図1は,mixiのソースツリーにおけるコードと,そのテストコードの毎月1日のバイト数をグラフにしたものです。2008年の頭には少なかったテストが急速に増え,今年の5月にはコード量をも追い越しているのがわかります。

携帯電話向けmixiである「mixiモバイル」の開始が2004年,mixiニュースが2006年ですから,2008年当時のmixiも,それなりに大きなアプリケーションであったと言えます。今回は,そのような比較的大きく,歴史あるコードの中に,どうやって自動テストを浸透させていったのか,ある一例をお話していきます。

図1 ソースコードとテストコードのバイト数比較

図1 ソースコードとテストコードのバイト数比較

データベースとの依存を切り離す

mixiでは,ほぼすべての開発者がSSH経由でデータセンターのLinuxマシン上に接続しています。マシンは複数のVMに切り分けられ,それぞれ別の開発者が使用します。個々の開発者は,mixiのフロントエンドにあたる部分だけをそのVM上で起動し,MySQLのようなデータベース,memcached,画像配信系をはじめとするサブシステムについては,ほぼすべての開発者が同じものを共有しています。

mixi全体に自動テストを導入する際に,最初に障壁となったのがデータベースでした。一般に,テストを書きやすいコードには次のような性質があります。

  • 結果を左右する入力をどこから与えるべきかが明確である
  • 結果がどこに出力されるかが明確である
  • それ以外の副作用を持たない

しかし,2008年ごろのmixiのコードには,これらを備えていないものが多くあり,とりわけデータベースは,テストに対する暗黙の入力であり,出力先になってしまうことがしばしばありました。誰かが友人関係を解消したり,日記を削除したり,といった行動によってテストが失敗してしまうようでは,テストが失敗することは日常茶飯事になり,テストを書く利点が減ってしまいます。また,テスト自体はうまく動作していても,副作用として,開発環境上の誰かの日記が増えたり,どこかのコミュニティのトピックが消えたり,ということも時折起こっていました。

これらの問題を解決するべく,2008年12月ごろにMixi::Test::Fixturesという新しいライブラリが実装されました。Mixi::Test::Fixturesは,RailsやDjangoにある「fixture」のしくみを模したライブラリで,具体的には次のように動作します。

  • テストの実行直前に一時的なデータベースを作成し,各種コードからの接続先をそちらに向ける
  • 指定されたYAMLからデータベースに値を挿入する
  • テストを実行する
  • テストの実行直後に,最初に作成したデータベースを削除する

当時のmixiでは,SQLのかなりの部分が手作業で書かれていて,O/RマッパはおろかSQL::AbstractやSequelに類するクエリ生成ライブラリも存在していませんでした。そのため,SQL文のかなりの部分がMySQLでしか動かず,また変換するにもフックポイントがありません。結果として,一時的なデータベースにもSQLiteのようなプロセス内で動作する軽量な実装を避け,MySQLをそのまま使っています。

Mixi::Test::Fixturesは,その後も継続的に改良が加えられています。今では,memcachedやメールの送信,外部へのHTTPアクセスなども実際の処理が走らないように変更できます。これらは,SQLが使えるようなRDBMSと比べるととても単純なため,Perlで書かれたプロセス内で動作する実装を作り,指定されればそれに差し替えるようになっています。

テストをすべて実行する

fixtureの導入によって,新しいテストは書きやすく,気軽に実行できるようになりました。一方でソースツリー全体を見渡すと,それ以前に書かれた,実行にいくつかの準備が必要だったり,共有のデータベースを書き換えてしまうようなテストも多く存在していました。

コードを変更したあとには,できる限り多くのテストを実行し,変更の正しさを確かめるべきです。実行しにくいテストを取り除き,「テストが失敗することは問題である」と自信をもって主張できる環境を早急に作るうえで,これらのテストの存在は邪魔になっていました。ただ一方で,これらの中には,事前の準備が必要なことを差し引いても,それなりに有用なものも多くありました。そこで,これらをひと思いに削除することはあきらめ,「ブラックリスト」を作ることにしました。

ブラックリストは,問題のあるテストの集合です。実際には,ソースツリー内にあるテストのうち,問題のあるものを列挙したリストをYAML形式のファイルとして作成しました。問題が解決したら,リストからそのテストを削除していきます。同時に,ブラックリストを使って「実行可能な(ホワイトな)テスト」を列挙するコマンドも作成しました。

始めからホワイトリストを作らなかったのには理由があります。ホワイトリストを作成する場合,

  • ①誰でもいつでも実行できるテストを作成した人は,それをリポジトリに追加すると同時に,ホワイトリストにも変更を加える
  • ②前準備が必要だったり,自分以外に実行されては困るテストを書いた人は,それを単にリポジトリに追加する

という作業が個々の開発者に要求されます。しかし,開発者としてより望ましい行動は①です。望ましい行動のほうが手間がかかるというのは,正しいしくみとは言えないでしょう。

図2は,導入から現在に至るまでのブラックリストの行数です。340台から一気に減少したあとは,細かな増減を繰り返しながら200台近辺で落ち着いています。テスト全体の件数は増えているので,ソースツリー全体に占める割合は下がり続けているものの,数として下げ止まってしまったのは今後の課題です。

図2 ブラックリストの行数の推移

図2 ブラックリストの行数の推移

著者プロフィール

加藤和良(かとうかずよし)

(株)ミクシィ システム本部技術部

コメント

コメントの記入