ラベル Test の投稿を表示しています。 すべての投稿を表示
ラベル Test の投稿を表示しています。 すべての投稿を表示

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月13日月曜日

Objective-Cでprivateなインスタンス変数を外部から変更したり参照する方法

UnitTestなどでテスト対象のクラスのprivateな変数(属性)を参照したり変更したりしたい場合があります。
テストのためにアクセッサを追加したりするのはちょっとあれなので何かいい方法がないか調べてみたらObjective-Cだとテストフレームワークの力を借りるまでもなくあっさりできますね・・

Objective-CのKey Value Codingを利用します。

これはざっくり言うとアクセッサーを用意しなくても、
 setValue: forKeyとvalueForKeyを使えば、名前(Key)でクラスの変数にアクセスできるという代物です。

テスト対象はこのPersonというクラス。(メソッド名がひどいのは無視しましょう)


@interface Person : NSObject
{
@private
    NSString* name;
}

- (NSString*)getName;
@end

@implementation Person

- (NSString*)getName {
    return name;
}

@end

テストコードはこのようになります。

- (void)testName {
    Person * person = [[Person alloc] init];
    
    [person setValue:@"Taro" forKey:@"name"];
    
    GHAssertEqualStrings(@"Taro", [person getName], @"not equeal name");
}


2012年6月26日火曜日

iOSでOCMockでテストする【環境構築】

Objective-C用のMockライブラリ OCMockを使うための環境構築

環境:
 Xcode 4.3.2
   OCMock 2.0

まずここからダウンロード。(ソースコードからやるやり方は後日追記予定)
http://ocmock.org/

"Download"のタブからdmgファイルをダウンロードします。
http://ocmock.org/downloads/ocmock-2.0.1.dmg (2012/06/24現在の最新)

ダウンロードしたら開きます。
展開されて、iOS, OSX, Source の3つのディレクトリが現れます。

iOSを開きOCMockのディレクトリをドラックアンドドロップでプロジェクトに追加。

libOCMock.aも同じくプロジェクトに追加。(ここではOCMockディレクトリ内に入れます)

そうするとこのようになるはずです。(注:プロジェクトに追加する際に、Copy items into destination ...にチェックいれるのを忘れないこと)

TargetのBuild Phasesのタブを選択し、Link Binary WIth Librariesのセクションを開きlibOCMock.aを追加します。

#import <OCMock/OCMock.h>を発見できるように、
 Build SettingのHeader Search Pathに$(SRCROOT)を追加。

ライブラリをリンクするために
Build Settingのother linker flagに -ObjC -force_load $(SRCROOT)/OCMock/libOCMock.a を追加。
以上で環境設定は完了です。

試しにテストコード(SenTest)に

#import < OCMock/OCMock.h>


- (void)testExample
{
    id tableViewMock = [OCMockObject mockForClass:[UITableView class]];
}

を記載して、Cmd + Uでテストを実行してみましょう。


2012年6月18日月曜日

iOSでGHUnitでテストする【環境構築】

GHUnitの環境構築方法です。

環境:Xcode4.3.2

STEP1. プロジェクトの作成

適当にTestGHUnitというプロジェクトを作ります。

STEP2. GHUnitの取得

2つの方法があります。git使っている人や男気があるひとは後者を使うといいと思います。

方法その1.
ビルドされてるのをそのまんまつかう。

https://github.com/gabriel/gh-unit/downloads からGHUnitIOS ***.zipをダウンロードしてくる。(上のほうの新しいやつのほうがいいと思います. 0.5.2で確認)

解凍するとGHUnitIOS.frameworkというフォルダができているので
プロジェクト内のフォルダに保存。

ここではTestGHUnitの下にFrameworksというフォルダを作成してその中に入れておきます。

TestGHUnit/Frameworks/GHUnitIOS.framework

方法その2.
githubからソースコードをとってきて、自らビルドする。
メリットとしてはgitですぐに最新版にアップデートできる点。

$ cd TestGHUnit/
$ ls
TestGHUnit TestGHUnit.xcodeproj TestGHUnitTests
$ mkdir Frameworks
$ cd Frameworks
$ git submodule add https://github.com/gabriel/gh-unit.git
Cloning into gh-unit...
remote: Counting objects: 7028, done.
remote: Compressing objects: 100% (2426/2426), done.
remote: Total 7028 (delta 4687), reused 6667 (delta 4360)
Receiving objects: 100% (7028/7028), 39.60 MiB | 268 KiB/s, done.
Resolving deltas: 100% (4687/4687), done.
$ cd gh-unit/Project-iOS

makeします

$ make

makeしたら以下のようなエラーがでる場合があります。

Error: No developer directory found at /Developer. Run /usr/bin/xcode-select to update the developer directory path.

Xcode4.3からXcodeのパスが変わっているためxcode-selectで/Developerのほうを見に行ってミスっている

$ sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer

で治る

STEP3. ビルドセッティング

まずテスト用のターゲットを追加します。
プロジェクトセッティング画面を開いて左下のAddTargetボタンを押し、EmptyApplicationを選びます。
ProjectNameを適当にUnitTestなどとします。
Use Core Dataや Include Unit Tests などにはチェックいりません。
次にTARGETSのな家から作成したUnitTestを選択し、BuildPhasesの画面を出します。
Link Binary With Librariesのセクションを開き下の+ボタンを押してフレームワークを追加します。
下のAdd Otherを選択します。
ビルドしたものをダウンロードした場合はTestGHUnit/Frameworks/GHUnitIOS.frameworkのフォルダを選択してOpenを押す。
自前でビルドした場合は、gh-unit/Project-iOS/build/Framework/GHUnitIOS.frameworkを選択。
そうするとこうなるはず。
もしも別の表記になっていたり、アイコンが違ったりしたら - ボタンを押して一回消してから上のをやり直すと治ったりします。
次にBuild Settingを開き、Other Linker Flagに -ObjC -all_load を追加します。
これでテストを書けば動く状態になっているはずです。

STEP4. テスト
UnitTestのフォルダ内のAppDelegate.h(ここではYYAppDelegate.h)を消します。

そしてYYAppDelegate.m を以下のように変更

#import <GHUnitIOS/GHUnit.h>
@interface MyTest : GHTestCase {   
}
@end

@implementation MyTest
- (void)testStrings {
    NSString *string1 = @"a string";
    GHTestLog(@"I can log to the GHUnit test console: %@", string1);
    
    // Assert string1 is not NULL, with no custom error description
    GHAssertNotNULL(string1, nil);
    
    // Assert equal objects, add custom error description
    NSString *string2 = @"a string";
    GHAssertEqualObjects(string1, string2, @"A custom error message. string1 should be equal to: %@.", string2);
    GHAssertEquals(1, 1, @"test");
}

- (void)testNums {
    GHAssertEquals(1, 2, @"test");
}
@end

main.m を以下のように修正

        return UIApplicationMain(argc, argv, nil, @"GHUnitIOSAppDelegate");

そしてテストを実行。
UnitTestのiPhone Simulatorを選んで、 Runボタンを押せば完了です。


2012年4月28日土曜日

Objective-C(iOS)のテスト関連のツールまとめ

Objective-C(iOS開発向け)テストフレームワーク

多いのでメモがわりに随時更新。
Objective-Cはマイナーだから少ないと思ってたけど現時点で多いわっ!

XUnit系

OCUnit(SenTest)
XCodeにはいってる

GHUnit
https://github.com/gabriel/gh-unit

Catch
https://github.com/philsquared/Catch

BDD系(RSpec style)

Kiwi
https://github.com/allending/Kiwi

Cedar
https://github.com/pivotal/cedar

ODCSpec
http://paytonrules.com/OCDSpec/

受け入れテスト系(selenium,cucumber style)

UIAutomation
Xcodeにはいってる

KIF
https://github.com/square/KIF

Frank
http://www.testingwithfrank.com/

Monkey Talk for iOS
http://www.gorillalogic.com/testing-tools/monkeytalk

NativeDriver
http://code.google.com/p/nativedriver/

iCuke
https://github.com/unboxed/icuke

UISpec
http://code.google.com/p/uispec/

Zucchini
http://www.zucchiniframework.org/

Test Studio for iOS
http://www.telerik.com/automated-testing-tools/ios-testing/ios-application-testing.aspx

delight.io
http://delight.io/

Calabash iOS
http://blog.lesspainful.com/2012/03/07/Calabash-iOS/

テストアプリ配布

TestFlight
https://testflightapp.com/

2012年3月4日日曜日

JenkinsでXcodeで作ったプロジェクトのビルドとテスト

JenkinsでXcodeで作ったプロジェクトのビルドとテスト(OCUnit, SenTestKit)を自動実行させる際の設定です。

SCMからソースコードを取得するところは他と同じなので割愛し、Xcode独自のところに絞ります。

まずはXcode Plugin を入れます。他にもSICCI Xcode pluginがあるみたいですが、私が試したかぎりでは動かすことさえできなかったです。(Jenkins 1.424.1)

設定すべきところは以下です。

ビルドの手順の追加でXcodeを選択し、以下の3つを設定します。
Target,SDK,Configuration。

SDKには必ずsimulatorのどれかを選択しないとテスト結果が出力されません。
注)Xcode4.3からXcodeのインストールディレクトリが/Developerから/Applicationsに変わりました。
SDKの指定は以下のように変更する必要があります。
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.1.sdk

残りはJUnit結果の集計にチェックを入れ、
テスト結果XML のところに test-reports/*.xml を入力しておけばよいです。

これでビルドを実行すればOKです。

設定直後だとテスト結果のグラフがでないことがありますが、その時はもう一回保存しなおせば出たりします。(C#でやったときもそうだったのでJenkinsのバグ?)