2012年9月19日水曜日

WindowsPhoneの非同期なRxをユニットテスト



コールバックメソッドを利用することよりも、Rxによってメソッドチェーンを記述することで、

コードが分散せず一貫したコードが書けるように思えます。

もちろん、ユニットテストは必要なのですがいかんせんWindowsPhoneはテストフレームワークが公式に

提供されているわけでは無いらしく、色々と面倒くさいです。

どうにか、記述する方法を発見したので公開。と言っても、neueccさんのところで公開されていますが・・・。

1.WindowsPhone7.1のプロジェクトを作る


作りましょう。

2.テスト対象のコードを記述する。


今回は、引数のURLにアクセスを行いページの内容をstringで取得するメソッドを用意しました。


3.テストプロジェクトを作成する。


3.1. 


まずは、WindowsPhone7.1のプロジェクトを作ります。

3.2. 


http://www.jeff.wilcox.name/2011/06/updated-ut-mango-bits/ で配布されているファイルをダウンロードし、参照に追加します。

3.3. 


テスト対象のプロジェクトを参照に追加します。

3.4.


 http://blog.richardszalay.com/2011/08/08/writing-asynchronous-unit-tests-with-rx-and-the-silverlight-unit-testing-framework/
こちらに記述されている WorkItemTestScheduler  と ObservableExtensions を追加します。

4. テストを記述する


今回は次のコードを用いてURLに適切にアクセスができているのかを確認します。


Assert.IsNotNull() とちょっと手を抜いていますが・・・。


5. 実行する

ソリューションを右クリックし、「スタートアッププロジェクトの設定」を選び、

テストプロジェクトをスタートアッププロジェクトに設定します。



Ctrl+F5でデバッグなし実行を行います。テスト結果は次のように出力されます。格好いいですね。

成功したパターン
失敗したパターン

6.所感

このやり方を発見するまでに時間がかかってしまった。結局やりたいことは例によってテストやビルドの自動化。

http://blogs.msdn.com/b/francischeung/archive/2012/01/03/running-windows-phone-unit-tests-via-msbuild.aspx

このサイトを見る限りMSBuildを利用して、コマンドラインからテストの実行ができるらしい? ので、いつもどおり

Jenkinsさんと組み合わせてCI環境を作るところまでやりたいです。

2012年9月10日月曜日

JenkinsでAndroidのユニットテスト+リリースビルドまで自動化


そういえば、LinuxでJenkinsを動かしてAndroidのビルドとかやってたけど、

記事にまとめて無かったや。

やったこと
・前準備色々
・Jenkinsの設定をする(OS:ScientificLinux)
・Androidのアプリのプロジェクトを複数のOS,ディスプレで自動でデバッグビルドする
・Androidのアプリのテストプロジェクトを自動でビルドし、実行する
・Androidのアプリのプロジェクトをリリースビルドして、回収する。
・テストのカバレッジを←文字化け発生する


前準備
・ライブラリ群のインストール
yum -y install glibs-devel.i686 zlib-devel.i686 libstdc++-devel.686

何よりもここが面倒くさかった。

・antのインストール
#wget http://archive.apache.org/dist/ant/binaries/apache-ant-1.8.3-bin.tar.gz
#tar xzf apache-ant-1.8.3-bin.tar.gz
#mv apache-ant-1.8.3 /usr/

・Androidプロジェクトを作る
ディレクトリの構成を
-Root--AppProject
--TestProject
--hoge.keystore

としておくと色々捗ります。

JenkinsやJDKのインストールは終わっているということにしましょう。

・Jenkinsの設定。
今回は、Gitのリポジトリをポーリングします。
・公開鍵の作成
#sudo -u jenkins ssh-keygen -t rsa
#sudo -u jenkins cat /var/lib/jenkins/.ssh/id_rsa.pub

こうして出来上がった公開鍵は、自分の環境に合わせて鍵の登録とかやっちゃってください。

・プラグインのインストール
Jenkins Gti Plugin
Android Emulator Plugin
をインストールします。

・AndroidSDKの設定
Jenkins→設定 から
プラグインがインストールされていると、現れる
Android
の項目。
AndroidSDK root:/var/lib/jenkins/tools/android-sdk を入力
Automatically install Android components when required にチェック





・antの設定
Ant項目の
名前:ant1.8.3
ANT_HOME:/usr/apache-ant-1.8.3

キャプチャは1.8.4

・プロジェクトを作る
新規ジョブの作成 から
ジョブ名:なんか適当
マルチ構成のビルド を選択

ソースコード管理システム
Git
Repository URL: git@hogehoge
Branches to build **
リポジトリブラウザ:自動

URL抜いたから赤字出てる。気にしないで。

ビルドトリガ
SCMをポーリング
スケジュール;1-59 * * * *

毎秒確認ってことですね。

マトリクスの設定
値の追加→ユーザ定義
名前:OS
値:2.3.3 3.0

名前:display
値:WVGA HVGA WXGA

名前:density
値:170 240



組み合わせフィルタ
フィルター:(!(OS!="3.0"&&display=="WXGA"))&&(!(OS!="2.3.3"&&display=="HVGA"))&&(!(density=="170"&&OS!="2.3.3"&&display!="HVGA"))&&(!(OS=="3.0"&&(display=="WVGA"||display=="HVGA")))







ビルド環境
Run an Android emulator during buildにチェック
Run emulator with properties にチェック

Android OS version:${OS}
Screen density:${density}
Screen resolation:${display}
Device locale:ja_JP


Common emulator options
Show emulator window のチェックを外す

これ付いてると詰むよ

ビルド
#アプリをサーバーのantでビルドできるように設定する。
ビルド手順の追加→シェルの実行
cd ${WORKSPACE}
export PATH=$PATH:/var/lib/jenkins/tools/android-sdk/tools:/usr/apache-ant-1.8.3/bin

echo Update Project
android update project -p ./ProjectName
echo Update Test Project
android update test-project -p ./ProjectName -m ${WORKSPACE}/Appname

echo Setting Release
cd ${WORKSPACE}/ProjectNamer
echo "key.store=${WORKSPACE}/hoge.keystore" >> ant.properties
echo "key.store.password=hogehoge" >> ant.properties
echo "key.alias=hoge" >> ant.properties
echo "key.alias.password=hogehoge" >> ant.properties

秘密の情報無いよな・・・

#アプリのデバッグビルドを行う
ビルド手順の追加→Antの呼び出し
使用するAnt:ant1.8.3
ターゲット:clean debug install
ビルドファイル:${WORKSPACE}/ProjectName/build.xml
#テストプロジェクトのビルドと実行を行う
使用するAnt:ant1.8.3
ターゲット:clean emma debug install test
ビルドファイル:${WORKSPACE}/TestPorject/build.xml
#リリースビルドを行う
使用するAnt:ant1.8.3
ターゲット:clean release
ビルドファイル:${WORKSPACE}/ProjectName/build.xml
ビルド後の処理
ビルド後の処理を追加→成果物を保存
保存するファイル:**/ProjectName/bin/*release.apk


うぉーなげぇ

プロジェクトのトップに戻ると、構成マトリクスができています。
フィルタの設定を確認するには、要素の上にマウス持って行ってリンクが貼られてたら、
フィルタされてない場所ってことみたいです。

さて、Android4.0.3からAPI(CPU?)を洗濯する必要があるわけですが、
エミュレーター設定のABIの項目に変数を入れても、
エミュレーター作成時にうまく読み込んでくれないみたいです。

というわけで、多分4.0系統は別のプロジェクトにしちゃうのが正解なんじゃないですかねぇ?

多分。


2012年9月7日金曜日

C# + LINQでJSONをパースする


foursquareの'explore'のレスポンスのJSONを、Linq To Jsonを使ってパースします。ちなみにWindowsPhone。

シリアライズすれば早いのに・・・とか言わない。

またパースにはJSON.netを使います。http://json.codeplex.com/

必要なのは、'venue'データだけなので、先に保存用のクラスを作っておきます。

namespace ConflimJsonParser
{
    public class Venue
    {
        public string name { get; set; }
        public string address { get; set; }
        public string crossStreet { get; set; }
        public string city { get; set; }
        public string state { get; set; }
        public string country { get; set; }
        public string cc { get; set; }
        public double lat { get; set; }
        public double lng { get; set; }
        public string id { get; set; }
    }
}



で、パースの部分。

ちなみに元のJSONは https://developer.foursquare.com/docs/explore#req=venues/explore%3Fll%3D40.7,-74 をみておいてください。

namespace ConflimJsonParser
{
    public class MyParser
    {
        private string json = "省略";
        public void parse()
        {
            var str = JObject.Parse(json).SelectToken("response.groups").ToString();
            var venues = JArray.Parse(str).SelectMany(groups => groups["items"])
                .Select(items => items["venue"]).Select(venue =>
                {
                    var location = venue["location"];
                    return new Venue()
                    {
                        name = (string)venue["name"],
                        id = (string)venue["id"],
                        address = (string)location["address"],
                        crossStreet = (string)location["crossStreet"],
                        city = (string)location["city"],
                        state = (string)location["state"],
                        country = (string)location["country"],
                        cc = (string)location["cc"],
                        lat = (double)location["lat"],
                        lng = (double)location["lng"],
                    };
                });

            foreach (var venue in venues)
            {
                Debug.WriteLine(venue.name);
            }

        }

    }
}


やってみて

LINQいいよLINQ。

まとまったデータが欲しいならシリアライズしろ。

データというか、モジュールの結合が密接になっているように思います。そのへんがオブジェクト指向じゃないってことかな。

実は今回のコードは今作っているアプリからの引用なんだが、どうもなぁ。

この部分は、IObservable<string>を引数に取るメソッド内で利用される。そのため、かなりコードが違うものになっているのだ。

そりゃあ、引数が違えば実装が変わってくるのは一緒だけどここまで違うの!? って思うくらいの違い。

なんか再利用性が低いなーみたいなことを思ってしまってます。俺のスキルが足りないだけと言われたらそれまでだけど。

とは言え、Rxを使えば非同期処理もすっきり書けたり、コードを追いかけるのも楽になったりといいことも多いわけで。

まあ、ゆっくりと勉強していきますね。