2011年11月18日金曜日

PHPUnit vfsStream メモ

PHPを触りだして3日。
サポートのはずなのになんか気が付いたら俺主体なんだけど…

という訳でメモ

なんだかんだでテスト系は情報無いよなぁ…

最初使い方に詰まったvfsStreamですが、これは使いやすいのでは?
重要なのはvfsStreamで生成したファイルをvfsStreamのディレクトリにちゃんと登録することw

// setup 何も指定しなければvfs://root というディレクトリがある前提になるはず
vfsStream::setup();
// rootの取得
$root = vfsStreamWrapper::getRoot();
// rootディレクトリパスの取得 実際にis_dirがtrueを返す
$rootDirPath = vfsStream::url($this->root->getName().DIRECTORY_SEPARATOR);

//モックファイルの生成と登録
$file = vfsStream::newFile("hoge.txt");
$root->addChild($file); // これに気が付かなくてチョットハマった

//モックファイルパス
$path = $rootDirPath.$file->getName();

//あとはこのpathをテストしたい奴に渡して何かしら処理させた後
//$fileの中身を見たりすればOKな感じ?

とりあえず動作確認



Gistを初めて使うけどまさかPHPで使用するとは思わなかったよw
でもGistいいかも?

2011年11月17日木曜日

MACに PDT + PHPUnit + XAMPP + MakeGood 環境を構築してみた

前回に引き続き、今度はphp関連の開発環境を構築してみました。

まるで環境構築マニアみたいですが、phpはちょっと必要に駆られてなので…

本末転倒ですが別にエディタでもいいとは思います。
めんどいし。
もっというとXAMPPじゃなくて個別にインストールしたほうが
後々いいかもしれません。なんとなく。
だってどうせサーバ上ではXAMPPじゃないでしょ。

とはいうもののPHP書いたこと無いし、vimさんだけで生きて行けるほど強くないし、
IDE無いとミスっていることにすら気が付かないし(ぇ



◯以下をとりあえずDL
PDTはプラグインだけでもOKですが、
なんとなくこれ以上プラグインを追加したくなかったので、
All IN One (eclipse-php?)を落としました。

XAMPPは上記サイトに従って動作確認までは余裕かと。



◯XAMPP上にPHPUnitをインストール

とりあえず以下をターミナルで実行
パスからも想像出来るようにルート権限必要です。

#cd /Applications/XAMPP/xamppfiles/bin
#sudo ./pear upgrade-all
#sudo ./pear channel-discover pear.phpunit.de
#sudo ./pear install phpunit/PHPUnit
インストールされている気配が無かったので再度実行
#sudo ./pear install phpunit/PHPUnit
Unknown remote channel: pear.symfony-project.com
と出たので
#sudo ./pear channel-discover pear.symfony-project.com
#sudo ./pear install phpunit/PHPUnit
#./phpunit --version
バージョン表記されてメデタシ(2011年11月15日 3.6.3でした)

依存関係をまとめてinstallできるpearのコマンドオプションがあった気がするけど
メモ忘れました…



◯XAMPP上にtestrunner
#cd /Applications/XAMPP/xamppfiles/bin
#sudo ./pear channel-discover pear.piece-framework.com
#sudo ./pear install piece/stagehand_testrunner



◯ /Applications/XAMPP/xamppfiles/bin にパスを通す
先にやればよかった…



◯他

またDB関連もテストするなら下記も入れておくといいかも

#sudo pear install phpunit/DbUnit

http://www.phpunit.de/manual/3.6/ja/database.html



◯PDT関連
makegood追加(help → install new software)
http://eclipse.piece-framework.com/
-clean で再起動



◯これは正直いいのかわかんない
PDT使ってPHP作ってローカルで動作確認したいのに、
当たり前といえば当たり前なんだけど、/Applications/XAMPP以下が軒並み読取り専用です。
結果としてリンク先にあるように最初から準備されているドキュメントルートに、
Eclipseがファイル配置出来なかったりします。
なので、httpd.confをいじって適当な場所にドキュメントルートを移動させるか、
最初から配置されている場所に書き込めるようにするかの2択だと思います。

ちなみに後者のhttpd.confを弄ってちゃんとうまく動くかは確認していません。

そして本当に読取り専用じゃなくしてしまっていいのかは知りませんw

まあ一番最初にpear実行段階で管理者権限でファイルイジりまくっているから
いいっちゃいいんでしょうけど…

なおeclipse-phpのPreferences → PHP → PHP Server → Edit Server に
Path Mapping ってあるんですけど、これうまく動いている気がしない。
これが動けば別にどこに配置したっていいんでしょうけどねぇ…



◯とりあえず作ってみる

まず悩みながら下記パスにワークスペースを作成

新規作成→PHPプロジェクト→hellophp.php / hellophpTest.phpを作成
環境設定でpearの設定を入れておく
(多分php.iniとかにinclude_pathを追加で指定しても一緒)



一応マニュアルを日本語のURL指定にしてみる。
まあしたんだけど英語しか出ないのはなぜなんだろ…

phpのパス指定

以下「個別プロジェクト」で設定
phpunit.xmlは指定しなくてもOK
下のテストフォルダの追加を忘れずに



include pathにさっき追加したpearを追加

上記で設定は終わり
hellophp.php

hellophpTest.php
assertEquals("hoge", $hello->hoge());
    }
}
?>
phpunit.xml



    
        
        
    


んで右クリック→すべてのテストを実行すると

やった!グリーンだね!

ちなみにこやつは設定次第では保存時に毎回全テストを実行してくれるので、
知らず知らずのうちにデグレが避けれる?
まあなんかうざい時もあるけど、ちょっとしたCI気分が味わえます。

なお、phpunit.xmlを上記のように設定していると
テスト完了後に指定したパス(ここでは/tmp/hello_report)以下にコードカバレッジ結果も。
phpunit.xmlは色々と設定ができそうで面白そうです。
Jenkinsなんかと合わせ安いんじゃなかろうか。
Jenkins使ったこと無いんで想像で書いてますが。そして使ってみたいけど…
上記を抜けなくやったのに、テストが実行されない場合は
一旦eclipse-phpを再起動するとなぜか動く場合も…

その前にコマンドラインでPHPUnitが使用可能かは確認してもいいかも。

まあこんだけやったけど、俺がPHPを書くのって多分今だけな気がする…
てかなんでPHP書く事になったんだっけ…

2011年11月6日日曜日

AndroidMock環境構築メモ 〜java.lang.RuntimeException: Could not find mockに嵌っている人へ〜

最近ようやくテストを書くようになりました、こんにちは。
以下はAndroidMockを使用する上でのメモ。

Android Test部による翻訳ページやtestterTestのソース、
そもそもAndroidMockのページのAndroidMockinEclipse.pdfを見てもうまく行かなかった俺向けmemo。

誤解を恐れずに言うとそもそものpdfが間違っているというか不親切。たぶん、きっと、もしかしたら…
ちなみに「interfaceに対してmockを生成するだけ」なら
  • AndroidMockRuntime.jarをプロジェクトに追加するだけ
でOK


まず陥ったこと
classのモックを生成しようとすると
java.lang.RuntimeException: Could not find mock…
が発生

ここではinterfaceでもclassでもMock作るよ〜的なこと書いてあるし、
こっちではMyClassとかいうclassのMock作っているしなんで??

とまあ結局こいつを解消するのに一晩掛かったというか、朝6時になった…/(^o^)\

以下アホみたいにスクショを混ぜて設定方法
動作確認環境は
  • Mac Book Air OSX 10.7
  • Eclipse 3.7
  • X06HT
  • INFOBAR A01
  • XOOM
AndroidMockのバージョンは1.1.1


まずMockを生成したいテストプロジェクトのプロパティを選択


AndroidMockRuntime.jarをプロジェクトに追加
Generator.jarではない。

後々プロジェクトを他人と共有したりするのなら、
プロジェクト以下の適当なディレクトリ(libとかlibsとか)に
コピって参照する方がいいかも。

繰り返すけど、Mockを生成する対象がInterfaceだけなら、
ここから下は設定してもしなくても一緒。


Java Compiler を選択してJDK1.6が選択されていることを確認

その下のAnnotation Processingを選択し、下図のようにチェックを入れる。
Enale processing in editorにはチェックを入れない



Annotation Processing の Processor optionsを下記のように修正

確かにpdfを見ると思わず
  • bin_dirにはテストプロジェクトのbinディレクトリへのフルパス
  • logfileにはファイル名のみ
とやりたくなるが、そうすると嵌る。

ここでは、
  • bin_dirにはテストプロジェクト/bin/classesへのフルパス
  • logfileにはテストプロジェクト/ AnnotationProcessor.logへのフルパス   
を指定したと仮定
俺が嵌ったところなので大きく書きます。


2011-11-08 追記
logfileを単にファイル名だけ記載すると
…/Eclipse.app/Contents/MacOS/ 以下に出来てました。
ってことは同ディレクトリ以下にワークスペース作れば(まあ作らないけど)
bin_dirも相対パスで書けるんじゃね?とか思います。
本当はもっと環境に依存しない書き方があればいいんですけど
ぱっとは思いつかね。



次にFactoryPathを設定
ここはpdfと一緒
AndroidMockGenerator.jarのパスを追加し忘れないように。

あとANDROID_RUNTIME の値はとりあえず手元の最新のandroid.jarを指定すればいいと思う。
俺の場合はr14だったのでICS用ですが、2.2~3.1端末で動きました。
→多分下方互換があるからなんだと思うけど。

また「自分で作成したクラスのMockが必須」であるのならば、pdfで言うAPP_UNDER_TESTを作らないとダメ。(名前はなんでもいい)
なので設定値は
  • テストプロジェクト/bin/classes
とかになるはず。
このAPP_UNDER_TESTに相当するパスを間違えると、たしかAnnotation.logに
「classが見つかんねーよ!」とログが出るはず。

その他外部ライブラリのモックを作るのなら、ここでそのライブラリへのパスを指定する。



と、ここまで設定したら、とりあえずお試しということで以下の様なソースを
適当に書いてみる。(テストとしては全く役に立たないけどw)
/**UsesMocksを忘れないように*/
    @UsesMocks(HogeUtil.class)
    public void testMockTest() {
        HogeUtil mockUtil = AndroidMock.createMock(HogeUtil.class);
    }

HogeUtil.classは元プロジェクトにあるなんかのクラスと仮定してください。
@UsesMocksが無くてもコンパイルは通りますが、下記が生成されません。

上記でビルドすると
bin_dirで指定したディレクトリ以下に「genmocks」ディレクトリが出来ており、
  • HogeUtilDelegateInterface.class
  • HogeUtilDelegateSubclass.class
が生成されているのが確認できるはず。
もし出来ていなければ、設定がどっか間違っていると思われる。
Annotation.logになにかヒントが出ているかもしれないので、見るといいかも?

次にとりあえずJUnitを実行させて、緑になるのを確認したほうがいいと思う。
仮に
java.lang.RuntimeException: Could not find mock…
が発生するようなら、やっぱり設定が間違っている。


ちなみにAnnotationで@UsesMocksを指定しているが、Interfaceは無くても問題なく動く。

createMockのソース追いかければわかるけど、interfaceの場合はすぐEasyMockへ
クラス名渡してその戻り値を使っている。
一方でクラスの場合は、上記の生成したクラスファイルを探しに行っていることがわかる。

なのでinterfaceの場合はAnnotationを書かず、Generatorが動作せずに
classファイルを生成しなかったとしても何も問題ない。
どっちにしてもinterfaceの場合DelegateInterface/SubClassどっちも作成されないけど。

結果最初に書いた、「InterfaceのMockを作るだけならRuntime.jarを追加するだけでOK」につながる。



なぜAndroidMockGenerator.jarでなくて、Rutime.jarをプロジェクトに追加したか?

実はどっちも試しました。
一晩あれこれ嵌ればそりゃあ色々と試しますw
結果どっちも同じように動きました。

ここからは単なる仮定なんですが、ファイルサイズからすると
Runtime.jarのほうが小さい。
でまあ名前とFactory Path の設定から
  • Generator.jarはDelegateInterface.class/DelegateSubclass.classを生成するための実行ファイル
  • Runtime.jarはAndroidMockをAndroid上で動かすために必要な最低限のクラスファイルの塊
なんだと思います。
当然処理が被るから、Generator.jarにはRuntime.jarも包含していると。

まあ細かく見てないから知らないけど多分そんな感じ。


以下NGな場合
bin_dirにテストプロジェクト/binを指定すると確かにgenmocksディレクトリが
bin以下に生成される。

ただし、この生成されたclassがテストプロジェクトによって生成されたapkに
含まれることがないようだ。
この状態だとAnnotationProcessor.logを見ても、きちんと生成されているので
実際に動作させて「createMock」を呼んだ所で
java.lang.RuntimeException: Could not find mock…
が発生する。

InterfaceのMock作成の場合はそもそもMockGeneratorが
事前生成しない(する必要がない)ので発生しない。

ただし、bin/classesを吐き出し先とするとEclipse上からは
フィルタがかかっているのようなので見えない。
→多分解除できると思うけどFilterにそれっぽいのが見つからない…

これが嫌ならテストプロジェクト/hoge ディレクトリとかを吐き出し先として、
そのhogeディレクトリをリンクさせてもOK
ただしクリーンをかけてもhogeディレクトリが対象にならないので、
自前でgenmocksディレクトリを削除する必要がある。

当然bin/genmocksをコンパイル対象にすればいいじゃんと思ったが、
実際に行うとコンソールにこっそりと下記が出力され、やっぱり
java.lang.RuntimeException: Could not find mock…
が発生する
trouble processing:
[2011-11-06 14:25:31 - HelloMockTest] Dx class name (genmocks/jp/omokageru/dnk/hellomock/HogeUtilDelegateInterface) does not match path (jp/omokageru/dnk/hellomock/HogeUtilDelegateInterface.class)
...while parsing jp/omokageru/dnk/hellomock/HogeUtilDelegateInterface.class
...while processing jp/omokageru/dnk/hellomock/HogeUtilDelegateInterface.class

結局genmocksディレクトリを直接指定してはダメで、
genmocksディレクトリの親ディレクトリを指定するのだが、
binディレクトリはすでに直下のclassesディレクトリをパスとしているので
binは指定できない。



以下疑問
残った android_framework_mocks.jar っていつ使うんだろ…
想像するに名前とサイズからAndroid特有のクラスのモックを作る際に必要なのかな?
いやでも、android.jarを指定させているからそこから参照して勝手に作りそうな気もするけど…

まあきっとなんかで詰まったらFactoryPathにでも指定すれば解決する日が来るのだろうw

もしなんか間違っていたりしたらご指摘ください。