2012年9月18日火曜日

Objective-Cのクラスメソッドの差し替え

テストのときにUIAlertViewの表示を消してくれるライブラリで
どうやってUIAlertViewのメソッドをフックしてるのかと調べてみたところ
method_exchangeImplementations()を使って実装されてた。


+ (void)swapInstanceMethodsForClass: (Class) cls selector: (SEL)sel1 andSelector: (SEL)sel2 {
    Method method1 = class_getInstanceMethod(cls, sel1);
    Method method2 = class_getInstanceMethod(cls, sel2);
    method_exchangeImplementations(method1, method2);
}
method_exchangeImplementations()を使えばこのようにクラスメソッドを差し替えられるのでテストでは便利。

2012年9月16日日曜日

XP祭り2012に参加してきた

XP祭り2012に参加してきました。

去年は参加だけだったので、今回はアウトプットをしようということで
LTで登壇してきました。


Xp祭り2012 lt leanstartup from Yasuharu Yanamura

これはUT Startup gymという半年間でアイデアをローンチまでするというプログラムの一プロジェクトとして僕が体験したことを簡単にお話しました。

実際のところ僕らのサービスは、まだローンチまでには至っていないので「やった」とはいいきれないし、
リーンスタートアップを教科書通りにやったわけではなく、考え方などを参考にしてやった程度なので、いろいろつっこみどころはあるとは思いますが、それでも実際にやってみたことで思ったとおりにはいかないことはたくさんあったので、「ひとつの失敗事例」としては他の人にも価値があるのではないかと考え発表させていただきました。

リーンスタートアップもアジャイルもこれといった答えはなく、あくまで「考え方」や「姿勢」のフレームワークだと僕は思っているので、この発表でお話した、「こうすればよかった」や「こうすべき」という意見は、あくまで「この家計簿プロジェクトに限る話」なので、全てに通じるわけでは全くないと思っています。

ソフトウェア開発と同様にスタートアップにも銀の弾丸はないので、試行錯誤してやっていくしかない。なので「超早く回す」ってことが一番重要だと思いますので、実際リーンスタートアップなことをやる場合はそうしたほうがいいのではないかと思います。

2012年9月14日金曜日

iOSでKiwiでテストする【基本編2】

つぎはテストコードの中身の書き方です。

it のBlocksの中にテストコードを書きます。

[テスト対象のオブジェクト should] マッチャー:期待される値];

という書き方が基本です。

マッチャーはequalなどですが、詳細はこちらを参照してください。
https://github.com/allending/Kiwi/wiki/Expectations

注意する点としては、
Objective-Cではプリミティブな型(intなど)はオブジェクトではないので、
theValue()で囲ってやることによってオブジェクトにしないとだめです。

SPEC_BEGIN(NSDateSpec)

describe (@"NSDate", ^{
    context (@"When October", ^{
        describe (@"NSDate#lastDate", ^{
            context (@"With year:2012", ^{
                it (@"should be 30", ^{
                        // ここにテストコードを書く
                        NSDate* date = [NSDate dateWithGivenYear:2012 month:10];
                        [[theValue([date lastDay]) should] equal:theValue(30)];

                });
            });
        });
    });

});

SPEC_END

・各テストで共通の前処理、後処理の書き方

以下を必要な階層に入れればよいはずです。

 beforeAll(^{ // Occurs once }); 

 afterAll(^{ // Occurs once }); 

 beforeEach(^{ // Occurs before each enclosed "it" variable = [MyClass instance];     }); 

 afterEach(^{ // Occurs after each enclosed "it" });

・ヘルパーメソッドの書き方

例えばこのような場合青字のところはヘルパーメソッドに置き換えたいといった場合の対応ですが、

SPEC_BEGIN(NSDateSpec)

describe (@"NSDate", ^{
    context (@"When October", ^{
        describe (@"NSDate#lastDate", ^{
            context (@"With year:2012", ^{
                it (@"should be 30", ^{
                        NSDate* date = [NSDate dateWithGivenYear:2012 month:10]; 
                        // 何か処理がいろいろ
                        // ...
                        [[theValue([date lastDay]) should] equal:theValue(30)];
                });
            });


            context (@"With year:1979", ^{
                it (@"should be 30", ^{
                        NSDate* date = [NSDate dateWithGivenYear:1979 month:10]; 
                        // 何か処理がいろいろ
                        // ...
                        [[theValue([date lastDay]) should] equal:theValue(30)];                });
            });
        });
    });

});

SPEC_END

これはBlocksを定義して、呼び出せばよいです。

SPEC_BEGIN(NSDateSpec)

describe (@"NSDate", ^{
    context (@"When October", ^{
        describe (@"NSDate#lastDate", ^{

            //Helper Method.
            NSNumber* (^helpMethodWithCount)(int) = ^NSNumber* (int count) {
                // 何か処理がいろいろ
                // ...
            };            
            context (@"With year:2012", ^{
                it (@"should be 30", ^{
                        NSDate* date = dateWithGivenYear:2012 month:10]; 
                        helpMethodWithCount(100);
                        [[theValue([date lastDay]) should] equal:theValue(30)];
                });
            });


            context (@"With year:1979", ^{
                it (@"should be 30", ^{
                        NSDate* date = dateWithGivenYear:1979 month:10]; 
                        helpMethodWithCount(10);
                        [[theValue([date lastDay]) should] equal:theValue(30)];
                });
            });
        });
    });

});

SPEC_END


^{}で囲まれているところは、そのまんまBlocksなので、その中に値を定義すれば、Blocksから参照できますので、ヘルパーメソッドに限らず定義できます。

iOSでKiwiでテストする【基本編1】

詳細はこちら。
http://www.kiwi-lib.info/specs.html

RSpecのObjective-C実装といったところで、
やはりRSpecと比べるとかなり機能が少なく最小限のようです。

基本的な書き方は、ほぼRSpecと同じなので、RSpecの本とか読めば参考になりそう。


使い方

テンプレートはこんな感じで、

#import "Kiwi.h"

SPEC_BEGIN(Spec名)

describe {@"テスト対象", ^{
    context {@"状態", ^{
        describe @"テスト対象メソッド", ^{
            context @"与える入力", ^{
                it @"期待する出力", ^{
                }
            }
        }
    }

}

SPEC_END

実際に書くと

SPEC_BEGIN(NSDateSpec)

describe (@"NSDate", ^{
    context (@"When October", ^{
        describe (@"NSDate#lastDate", ^{
            context (@"With xxx", ^{
                it (@"should be 30", ^{
                        // ここにテストコードを書く
                });
            });
        });
    });

});

SPEC_END

ちょっと例が悪かったのでインプットが思いつかなかったのですが、、
こんな風に書けばよいはずです。

あとはインプットや状態によって以下のようにテストを増やしていけばいいのです。

SPEC_BEGIN(NSDateSpec)

describe (@"NSDate", ^{
    context (@"When October", ^{
        describe (@"NSDate#lastDate", ^{
            context (@"With xxx", ^{
                it (@"should be 30", ^{
                        // ここにテストコードを書く
                });
            });
            context (@"With xxxxx", ^{
                it (@"should be 31", ^{
                        // ここにテストコードを書く
                });
            });
        });
        describe (@"NSDate#firstDate", ^{
            it (@"should be 1", ^{
                    // ここにテストコードを書く
            });
 

        });
    });
    context (@"When February", ^{
        describe (@"NSDate#lastDate", ^{
            context (@"With xxx", ^{
                it (@"should be 28", ^{
                        // ここにテストコードを書く
                });
            });
    });
});

SPEC_END

なにがよいかというと、テストを実行してみるとわかると思いますが、
出力を見てなんのテストが実行されているのかがわかりやすいのがメリットです。
xUnitでもやろうと思ったらできると思いますが、コピペを繰り返すことになるので、
このように階層構造でわけられるので便利だし、見やすいです。

2012年8月25日土曜日

iphone_dev_jp東京 mac/iphone Hackathonに参加してきた

使えるライブラリを作ろう!というハッカソンに参加してきました。

個人的に家計簿アプリを作っていてNSDateがかなりいけていない(面倒なのが多い)のでそれを楽にするものを作りました。

https://github.com/yanamura3/NSDateHelper

実装自体は自分のソースコードからライブラリ化したいものをひっぱってくるだけだったのでほぼ一瞬で終わってしまい、大半はKiwi使ってテスト書いてました。

できたものはライブラリというかカテゴリになってしまいましたが。

問題はカテゴリを組み込むのにgitsubmoduleやcocoapodsだと2つファイル読みこめばいいだけなのに無駄にプロジェクトファイル毎取り込まなければいけないのがなんだなぁと。この辺りもなにかうまいやりかたを考えていく必要がありそうです。

結構時間があまったのでOCMockitoというモックライブラリで足りない機能があったので追加してみました。

https://github.com/yanamura3/OCMockito

追加したのはスタブのメソッドが呼ばれた際に指定したblocksを呼ぶことができるようにする拡張で、

[given([mock somemethod]) answer:^{ /** 処理 */ } ]

と書けば somemethodが呼ばれるとanswerに渡したblocksが実行されます。

NSProxyのforwardInvocationの勉強にもなったので結構役に立ちました。


最後は発表があったのですが、あまりにもやったことが地味すぎたので
今度ハッカソンに参加するときはもうちょい見た目にインパクトあるものを考えて参加しようと思いました・・・


Togetter
http://togetter.com/li/361912

中島さんの問題

中島聡さんのブログにでていたザッカーバーグの面接試験解いてみました。

問題その1
http://satoshi.blogs.com/life/2012/08/block.html

回答
https://github.com/yanamura3/HTTPLoader

GCDを使って同期リクエストをラップして非同期に見せかけるという一番シンプル(実装の少なくて済む)なやり方で実装で実装しました。
問題としてはキャンセルはできないのでキャンセルもしたい場合は別のやり方のほうがいいです。


問題その2
http://satoshi.blogs.com/life/2012/08/zack2.html

回答
https://gist.github.com/3426890

これはretainカウントを別の用途に使うといった発想でおもしろいやり方だなぁと思いましたが、複数人でやるようなプロジェクトでこういう一見わからない実装を入れると間違った使い方がひょいと混入しそうで怖い感じがします。

2012年8月22日水曜日

【Ruby最低限の知識編】Ruby素人が「たのしい開発スタートアップRuby」を実践

結構ざっくりとした説明しか書かれていませんが、他の言語を使ったことがあると用意に理解できると思うので、短い割にちょうど良い程度の説明の仕方だなと思いました。
ただこれまで経験したことのないものがポンとでてくるとちょっと理解に苦しみましたがそこはちゃんとした言語の本を読んで、ここではこんなもんかとさらっと流すほうがいいかなと。

以下感想。

・変数を使う
 いたって普通
・四則演算
 いたって普通
・すべてがオブジェクト
 Java,C系統のようなプリミティブはない。動的型付けだとそうですよね
・文字列
 ' ' or " "
   + で連結できて楽
 #{ } で囲めば文字列の中に変数の値とかをぶっこめる
・メソッド呼び出し
 オブジェクト.メソッド Java、C#とかと同じ
 BOOLを返すメソッドは最後に ? をつける。いままではisとかhasのプレフィックスつけてたので新鮮。言語としてお作法が決まっているのはいいこと。
 メソッド() の()は略していいようだ(ただし()なしで解釈できる場合に限る・・)
・配列
 [] の中に型とか気にせずにつっこめる
 +で配列もつなげれる
 ーで差分をとれる
 * で繰り返しができたり
・ハッシュ
 { key => value , , }
・シンボル
 軽く理解に苦しむ。
 keyに文字列よりはシンボルを使うようだ、書き方がJSONっぽい
・コメント
 #
・真偽
 true, false, nil
・条件分岐
 ここはちょっと気持ち悪い、なぜこうしたし。
 elsifをタイポしそうだ・・
   switchではない

 if
   elsif
   else
   end

 case
   when
   when
 else
   end

   ifの1行での記載は他の言語でもみたことないしかなり読みづらい。これ嫌い。
 でもifを前にもってきても{}つけないから余計見づらいのか、なんなのか。
 だいたい1行で書く系は可読性犠牲にしているのでしょうがないか。

・繰り返し処理とイテレーション
 for文によるfor eachの書き方はC#,Objective-Cとかと同じ。

 each do ブロック end でなんぞこれ・・・
 each { | | 処理 } のほうが初見では理解できた。eachの引数に{}渡して毎回{}内が実行されるんだろうなと。
 
 ブロックをObjective-Cのblocksと同じと考えることで理解。
 Objective-Cだと ^(引数) { 処理 } なので
 rubyは | 引数 | 処理 ってことなんすね、たぶん。