2012-03-31 16:01:21
初参加の第 56 回以降, 3 回連続で参加・発表させていただいています.
今回は通常の発表枠で自作の MapReduce フレームワーク MyMR について紹介し, 懇親会でもスライド無しでの LT をさせていただきました.
PHP と MySQL でカジュアルに MapReduce する
最近書いたブログ記事と全くのどうタイトルで発表させていただきました.
内容的にも大体はそこに書いてあることで, 特に新しい内容は特に入れてません.
SlideShare 上では既にはてブがたくさんついていてありがたい限りなんですが, Hadoop もやったことないのに MapReduce を語るという大変イタい内容なので, 間違いなどがあれば @yuya_takeyama までご指摘いただければ幸いです.
MyMR についてはの紹介記事はこちらです.
Symfony Console 用テストライブラリ SpyOutput と Test Spy パターン, またはその命名問題について
こちらは懇親会 LT でスライド無しで発表しました.
即興と言えば即興ですが, 内容としてはブログにまとめた内容からになります.
大体以下のようなことを話しました.
- モックを使ってテストを書いた結果後悔する, ということは起こりがち
- テスト項目が実装の詳細に寄ってしまいがち
- Test Spy で済ませられるならその方が低コストで可読性/メンテナビリティの高いテストが書ける
- SpyOutput の紹介
- モックを使わないと書けないテストがあることも確か
- これは本当にモックを使うべきなのか, については熟考する必要がある
この日は @fivestr さんによるモックオブジェクトフレームワーク Phake についての発表があって, そこに触発された部分もあります.
以上前半の半分が発表で, 後半はこちらから会場に向かって質問させていただきました.
これも内容は SpyOutput の記事中に書いたもので, 「Symfony 等を拡張する単機能ライブラリを書いた場合, そのベンダープリフィクスはどうするべきか」というものです.
いただいた意見は以下のような感じです.
- 自分の名前を使う
- カッコいい名前を考える
- 自分の名前を使ったカッコいい名前を考える
いずれにせよ命名センスが求められるようです...
結論としてはとりあえず Symfony 本体に Pull Request を送ってみて, ダメならそこでベンダープリフィクスを検討する, という方向で考えています.
終わりに
今回は懇親会でもデザインパターンについてなど, いろいろ会話の場を持てて大変楽しい勉強会となりました.
主催の @gusagi さん, 会場提供の株式会社 VOYAGE GROUP さん, 本当にありがとうございました.
あわせて読みたい
2012-03-25 21:00:13
以前紹介した MapReduce フレームワーク MyMR の開発を少し進めました.
パッケージとしてのリリースはまだですが.
この記事では変更点等について紹介します.
MyMR の仕組みなどについては前回の記事を参照してください.
ビルダーによる Mapper/Reducer の定義
元々はクラスにメソッドを定義する形で行っていた Mapper/Reducer の定義を, ビルダーを使って定義できるようにしました.
この方法を使った場合, Mapper も Reducer も無名関数だけで定義できるようになったので, よりカジュアルになったと言えるでしょう.
また, クラスベースの定義では, 入出力のテーブルをコマンドラインオプションで指定していましたが, こちらではビルダーで指定できるようになりました.
なので設定ファイルの別出しすることもできるようになっています.
また, コマンドラインで指定した場合はそちらが優先されるので, テスト時はテスト用のデータベースを使う, ということもできます.
無名関数以外でも, call_user_func_array() で実行できるもの (PHP 5.4 で言うところの callable) であれば何でもいいので, インスタンスメソッドなどを Mapper として定義することもできます.
簡単なものは無名関数でサクッと定義してい, 複雑なものはユニットテストされたクラスのメソッドで定義する, といった使い分けができるようになっています.
クラスベースの定義も一応残していますが, ビルダーの方が圧倒的に使いやすいように思えるので, そのうち消すかもしれません.
コマンド名の変更
以前は mymr execute コマンドでクラスベースの MapReduce 定義を実行していましたが, ビルダーによる定義の追加に伴い mymr class コマンドに変更しました.
Emitter の切り出し
Mapper はもともと \MyMR \Base クラスの emit メソッドを $this->emit($key, $value) として読んでいましたが, 無名関数で同様のことをやるには PHP 5.4 以降の Closure::bindTo() を使用する必要が出てきてしまうため, Emitter クラスを別に作り, Mapper に引数として渡すようにしました.
これにより, Mapper のテスト時には Emitter をモックオブジェクトや Test Spy オブジェクトに差し替えてテストする, ということもできるようになりました.
進捗の表示
元々は実行しても無言で終了していましたが, Map と Reduce それぞれで進捗を表示するようにしました.
[caption id="attachment_1859" align="aligncenter" width="249" caption="進捗の表示"]
[/caption]
今回 \MyMR \Progress という形で実装しましたが, これ自体の MyMR との結合は緩く, Symfony の Console コンポーネントの OutputInterface にしか依存していないので, 別で公開できればと思っています.
まとめ
遊びで作り始めたものですが, 少しずつ様になって来ました.
まだまだ開発が安定しないので, インターフェイス等は大幅に変わる可能性があると思いますが, 引き続き開発を続けて行きたいと思います.
MyMR については, 3/27 の第58回PHP勉強会@東京で発表する予定なので, 後日スライドを公開する予定です. (今からつくる)
あと, MySQL Casual Talks Vol. 3 の LT 枠でも喋りたいと思っているんですけど, これってもう募集していないんでしょうか...
2012-03-24 19:45:49
Symfony2 はいわゆるフルスタックな Web アプリケーションフレームワークではありますが, 各コンポーネントは疎結合になっており, それぞれは Symfony Component として再利用できる形で公開されています.
その中でも Console コンポーネントは Behat や Composer, また Stagehand_TestRunner など, あらゆる PHP アプリケーションに利用されています.
その Console コンポーネントを使った際に, 出力のユニットテストを行うための SpyOutput というものを書きました.
これは でいうところの Test Spy という方法を使ってテストを行います.
yuya-takeyama / console-output-spyoutput
Test Spy とは
分類についてちゃんと理解できていない可能性もあるので, 今の所の自分の理解として書きます.
Test Spy オブジェクトはモックやスタブ同様, テストのために使用する偽物のオブジェクトの一種です.
本来使うべきオブジェクトの代わりにテスト対象に差し込んで, テスト対象の動作を行ったあとで, Test Spy に問い合わせることで検証を行う, というものです.
説明だけではわかりにくいと思うので以下の PHPUnit によるテストコードをご覧ください.
Application クラスはコンストラクタで OutputInterface (Console コンポーネントに含まれるインターフェイスです) を実装したオブジェクトを受け取り, 出力はそのオブジェクトに委譲します.
本来であれば ConsoleOutput 等をセットするところを, 代わりにこの SpyOutput をセットしています.
SpyOutput は OutputInterface を実装しているので, タイプヒンティングで OutputInterface が指定されていても問題ありません.
ConsoleOutput は write() や writeln() といったメソッドに文字列を渡すことで, その文字列を標準出力に出力しますが, SpyOutput は代わりにインスタンス変数内に溜め込みます.
Application オブジェクトの実行が完了したあとに, getMessage() メソッドで SpyOutput オブジェクトに問い合わせることで, 本来であればどのような出力がされるところだったのか, を問い合わせることができます.
車輪の再発明では無いのか
作ってから気づいたのですが, Console コンポーネントには既に出力をテストするためのクラスが存在していました.
Console コンポーネントはそもそもの謳い文句が "Console eases the creation of beautiful and testable command line interfaces." となっており, いわばこれらのクラスもひとつのウリである, と考えられます.
ちょっと考えましたが, それでも SpyOutput は私にとって必要だと思いました.
これらは Console コンポーネント標準の Application や Command といった, 粒度の荒い単位でのテストを想定して作られたものであり, それより粒度の細かい単位のコンポーネントがある場合は適用することができません.
そもそも私がこれを必要としたのは, あるコマンドラインアプリケーションにおいて, その進捗状況を表示する Progress というコンポーネントを書いていて, そのテストに必要だった, というのがあります.
Progress の使用イメージは大体以下のような感じです.
これを Command という単位でテストしようとした場合, Progress とは関係の無い出力までそこに含まれてしまう可能性があります.
その可能性をできる限り排除するには, Application でも Command でもなく Progress 自体をユニットテストするのが良いでしょう.
このように, SpyOutput を使うことで, Progress だけをテストすることができました.
Progress は Symfony の Console コンポーネントの Application や Command のことは知らず, OutputInterface のみにしか依存していません. 進捗表示機能を SomeCommand 自体に持たせるのに比べると, 疎結合で, 再利用性の高い設計と言えるでしょう.
命名問題
長々と SpyOutput について説明してきましたが, 実はこのライブラリまだきちんとしたリリースができていません.
というのも, ベンダープリフィクスをどうすべきか迷っていて, 今のところ Perl っぽく X をつけて SymfonyX などとしているためです.
Symfony のためのライブラリとはいえ, 勝手に Symfony というベンダープリフィクスを使うのは多分ダメだろうし, そうなると何というプリフィクスを付けるべきでしょう?
どなたか教えてください.
(Symfony 本体に Pull Request 投げて取り入れてもらえると楽なんだけど...)
まとめ
この記事では Symfony の Console コンポーネントの出力をテストするライブラリ SpyOutput と, Test Spy というテスト手法について説明しました.
命名問題が解決したら, Packagist 辺りに公開するつもりです.