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

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

0 件のコメント:

コメントを投稿