やらなイカ?

たぶん、iOS/Androidアプリの開発・テスト関係。

#TwitterFlock でFabricの話を聞いてきたメモ

Twitter社主催のアプリ開発者向けイベント"Twitter Flock Tokyo"に行ってきました。このイベントはTwitter社が提供しているFabricについてのもので、各都市で開催されています。

f:id:nowsprinting:20150520075937j:plain

Fabricではいくつかのサービスが提供されています。これまで、ベータ版配布サービス"Beta"、クラッシュログ解析サービス"Crashlytics"については導入事例も聞いており、興味を持っていました。

しかしそれだけでなく、ユーザ認証、Twitter連携、広告までひとまとめのSDKで提供されています。むしろ逆に、『広告SDKに、"Beta"、"Crashlytics"、ユーザ認証などの機能が無償でついてくる』くらいの印象を持ちました。

それぞれ(有償の)競合サービスが出ている中、これら機能を無償で提供して広告でマネタイズするということなので、頭ひとつ抜けたというか、もう別格と考えて良さそうです。

以下、セッションの内容のメモ。

Keynote

はじめに、サービスの全体像、Fabricの機能を一通り紹介。

実装サンプル"Cannonball"は、iOS/AndroidそれぞれGitHubにプロジェクトが公開されている。iOS版はSwiftで書かれている。

github.com

github.com

iOS/Androidとも、SDKの組み込みは非常に簡単。

日本企業の導入事例として、Cyber Agentさん、cameranさん、OneNewsさんの紹介。

Twitter主催のスタートアップコンペ"Hatch"の紹介。

DIGITS

  • DIGITSは、電話番号認証。Fabricの"Twitter Kit"に含まれる。
  • モバイルを前提としたサービスでは、電話番号認証はとても有効
  • これまでも類似サービスは存在したが、以下の理由で難しく、サービス利用も高額だった
    • SMS APIは国・キャリアによって異なる
    • 電話番号からキャリアを判断する必要もある
  • Twitterでは独自にこの機能を構築しており、9年の実績がある。DIGITSはこれを一般の開発者に無償で提供するもの。
  • 認証用ボタン、認証画面はSDKで提供。コールバックを書くだけで使える
  • 認証画面のbackgroundColor, accentColorなどカスタマイズ可能
  • userID, authToken, phoneNumberを取得可能
  • SMSメッセージはローカライズされる
  • AndroidではINTERNETパーミッションが必要
  • Androidで以下のパーミッションを許可すれば、1タップでログインも可能
    • READ_PHONE_STATE
    • RECEIVE_SMS
  • iOS/Androidネイティブアプリだけでなく、html5でも利用可能
  • digits.comで、電話番号変更やTwitterアカウントとの統合などを行える
  • 電話帳マッピングで友達のアカウントを探す

Crashlytics Kit

  • stability, testing, analyticsにまつわるサービスが統合されている
  • デバイス、OSバージョン、3G/WiFi、メモリなどの情報も収集
  • ユーザセションに属性をセットすることで、セグメント分析も可能
  • answersは、リアルタイムの分析エンジン。セットアップするだけでいくつかの指標で分析できる
    • ダッシュボードで見るほか、Emailでレポート
    • stability alert。安定度がしきい値を割るとアラートメールを送ってくれる
    • カスタムイベント(beta)
    • アプリのバージョンアップ当日、以前のバージョンとのクラッシュ率などを比較

Twitter Kit

  • "Fablic.with(TwitterKit)"で利用可能。様々なモジュールがあるので、"with"で宣言。"start"なども検討したけど"with"に。
  • Twitterアカウントによるシングルサインオン
  • シェア
    • TweetComposerクラスにチェインで指定。text().url().image().show()
    • 埋め込みツイート。リアルタイム、コンテンツの補完
    • TWTRTweetViewで表示もViewレベルで提供。サイズ、テーマなど設定が可能
    • TweetViewDelegete
      • willShareTweet
      • didSelectTweet
      • etc..
  • ゲスト認証(limitはある)
  • REST API Client
  • 使いかたはCannonballを参照

広告

  • オーガニック
    • Twitter Card。いくつか種類がある中に、App card。
    • アプリのページにmeta tagで書く
  • deep link
    • アプリのURLスキーム起動、Twitterカードからインストール通知を受けられる
    • カード表示のとき、URLスキームを見て「ストアで開く」か「アプリ起動」ボタンになる
  • full deep link
    • URLスキームに続けてパラメタを渡す
  • 分析
    • analytics.twitter.com
    • アプリのコンバージョン測定
    • Twitter card経由のインストールも計測できる
    • cardのインプレッション、アクティビティ
    • インフルエンサーと人気コンテンツを計測(インプレッション、インストール)。インフルエンサーに直接コンタクトできるのがTwitterの強み
    • CrashlyticsのアカウントとTwitterアカウントを連携することで、cardの計測が可能
  • プロモーション(paid)
    • セグメントを絞って広告を出せる。id指定もできるので、既存ユーザのみ、既存ユーザを除く、といった指定も可能。
    • ペイド:オーガニックには相関関係がある。ペイドの3.9〜5.2倍のオーガニック(インプレッションやクリック)が得られる

MoPub

  • 広告SDK
  • AdMob, iAdなど、他の広告ネットワークにも接続できる
  • 表示される広告は、MoPubと広告ネットワーク混在でオークションされ、高値の広告を表示できる

所感

評判の良い"Beta"、"Crashlytics"に、Analyticsの"Answers"もセットになってるなら使ってみよう、くらいに思っていましたが、他の機能も強力。

MoPubも筋が良さそうな印象で、広告SDKに対する締め付けも色々出てきている中、単に「セットだから」でなく、これ単体でも十分検討に値するのではないかと。

また、Twitter連携、DIGITSは、MBaaS各社とも競合しそうですね。この先もしPush通知などをサポートしてくると、MBaaSでなくFabricだけで足りるというアプリも出てきそう。 (Twitter社としては、Push相当のことをTwitterでやらせたいような気もするので、実現性は低いかも)

testotips.io(Appium回)に行ってきました #testotipsio

クックパッド株式会社さんで開催された、testotips.io 第1回*1に行ってきました。

connpass.com

testotips.ioは、iOS/Androidの開発系Tips共有会として続けられているpotatotipsのテスト版を指向していて、s.io==塩味*2という意味がある(かもしれない)とのこと。

今回のお題はAppium。スライドは追々connpassに張られていくと思うので、以下メモのみ。

Introduce Appium(松尾さん)

  • Appiumはnodeベースのツールで、iOS/Androidのネイティブアプリおよびブラウザの操作を自動化、GUIテストができる
  • GUIのテストについて。E2E test、Feature testなどと呼ばれているもの。テストケースの量は、Unit test > Integration test > GUI Test
  • 内容はappium.jsを追っていけばだいたいわかる
  • Capabilities*3でAppiumサーバの振る舞いを指定する
    • platform: ios, android, selendroid, chrome, safari, firefoxos
    • iOS: calendarFormat, sendKeyStrategyとかも指定できる
    • Android: unicodeKeyboard(マルチバイト文字を使う場合)
  • Tips
    • Appium 1.3.6では、iOSのplistがバイナリだと文字化けしていた。1.3.7betaで修正
    • Viewの取得、XPathだけでなく、UI Automation(iOS)/uiautomator(Android)の書式でも取得可能
    • Appiumサーバは複数実行できる(listen portを分ける)。ただしiOSはひとつしか実行できない
  • 実行には時間がかかるので、夜間にまわして翌朝チェックしている
  • DroidDriver

テスト結果レポートをSahaginで作ってみる(伊藤さん)

  • Sahagin==さはぎん。伊藤さんが作った
  • 今のところJavaのみ対応。もともとSelenium WebDriverのテスト結果のレポーターで、Appiumでも使用できる
  • テストコードから、日本語+画面キャプチャのレポートをhtmlで出力
    • スクショはステップごとに自動で撮ってくれる
    • findとかasertも日本語に置き換えて出力している
  • PageObjectパターンを使う場合、setterに@TestDocでコメントを書くとそれが表示に使用される
  • Jenkins Sahagin pluginで、Jenkinsのジョブ画面に表示できる
  • Selendroidだとスクショのタイミングがちょっとずれる
  • Tips
    • Jenkinsで実行するとき、テストの前後にAppiumサーバの起動と停止をすると安定する
    • Android emulatorのブラウザでスクショが取れない(Chrom Driverの問題?)
    • Xcode 6.2ではiOSシミュレータのSafariが使えない
    • Appiumのキャプチャが遅い
    • Appiumサーバはrootで起動できないので注意

AppiumのPHPクライアントを使う上で行っている工夫(高橋さん)

  • Yahoo!社内でPHPクライアントを使った自動化の試み
  • JavaScriptはコールバックが多くて嫌だった。社内のサーバサイドで多く使われていたPHPを選択。でも他のクライアントライブラリに比べて機能が足りない
  • テストフレームワークにはPHPUnit
  • Appium PHP Clientを継承したHelperクラスを作り、それを継承した個々のTestCase作成。Helperには足りない機能を書いている
  • コンストラクタでsessionStrategy=isolatedにされていたので、テストケースごとにアプリインストールが走っていた
  • sleepを極力しない工夫。findをラッピングして、例外を返さず、waitしてリトライすることをHelperに実装。テストコードをシンプルに保つ
  • swipeが座標指定なのをラッピング
  • UI操作のスモークテストにAppiumを使用。細かい操作のテストにはEspressoを使用している

Appiumを使ってiPhone Safari、Andorid Chromiumを使ってアプリをテストするTips(浅黄さん)

  • ブラウザのテストにAppiumを使用している
  • Androidは、SelendroidだとWebViewで実行されるので、最近はChromeDriverで直接Chromeを使用するようにしている
  • Selenium Grid Serverを使って実行。ただし、Appiumはキックできるけど、ChromeDriverはキックできないのでGrid Severを経由していない。
  • テストケースは、Excelの表形式で書いたものからJavaのコードを生成する。共通処理(ログイン等)、シナリオ、PageObject、それぞれExcelファイルで書ける

Running appium tests on Jenkins(Vishal Banthiaさん)

  • AppiumによるiOS/AndroidテストをJenkinsから実行するデモ
  • テストはRSpecで、失敗したらスクショを撮っている
  • buildとtestはジョブをわけて、pipelineでつなぐ
  • テストは機能ごとにジョブを分けている(これは増えていくとつらいと思うので私は否定派)
  • デモのリポジトリにはHeplerも入っている
    • servermanager.rb: Appiumサーバの管理。startで、前のプロセスをkillしたりとか。
    • screenshot_embedded_html_formatter.rb: スクリーンショット

所感

懇親会も込みで、他の会社で実際どのように運用しているとか、という話が聞けるのはとても有益でした。Tips紹介だけだとこれは得られないので、テスト自動化まわりはこんな感じに(ツール縛りくらいで)情報交換できる場はいいですね。

*1:とは言っていませでしたが続くといいですね!

*2:プロレスファンとしては「しょっぱい」に余り良いイメージを持てませんが、それはそれ。これはこれ。

*3:Capability==ケーパビリティ。Capacityはキャパシティなのに!

モバイル向けテスト手法勉強会に行ってきました #33testing

先週、クラウド名刺管理のSansan株式会社さんで開催された『最新事例から学ぶ!モバイル向けテスト手法勉強会』に参加させていただき、また「テストの種類とBDD」と題してお話してきました。

テストの種類とBDD

どうしても前提となる話が増えてしまってテスト実装の話まで入れられませんでした。このプレゼンがつまらなくても、テストのことは嫌いにならないでください。

後半は、BDDか、テクニック的な話(テストダブル、テストファースト、TDDとか)にするか迷ったのですが、今回の感触だとテクニックの話のほうがよかったかも知れません。しかし元々20分ギリギリ話すつもり&電エースの話をしすぎた結果、BDDの部分は全部飛ばすはめになった今となってはどちらでもよかったですね。

テストのテクニックに関しては、拙著『iOSアプリテスト自動化入門』の2.3、2.4に書いてありますので、そちらを参照頂けましたら幸いです。

なお、BDDのところで紹介しているテスト自動化ツールの連載は、昨日第6回が公開されました。

ほかの方々の講演

聴講メモが混ざってしまっていたので、以下まとめて全体の話として書きます。

テストを書いてるひとはまだ少ない
  • UIのテストを過剰に自動化しようとする(そして挫折する)よりはいいと思います
  • 岸川さんの話にあったように、モデル(MVCのM)を中心に、ユニットテストは自動化していったほうがいいでしょう
    • ただし、「6〜7割実装してからテストを書きはじめる」のはテストを書いたことが無い人が真似しようとしても難しいです。テストしやすい実装になっていないと、テストを後から書くのは困難なので。
  • ユニットテストのメリット、運用(岸川さん)
    • 変更を入れる際の安心感
    • テストはgreenな状態を維持する
    • カバレジはあまり気にしすぎない。テストを書くモチベーションになるなら使う
      • ユビレジさんの場合、サーバサイドで80%、クライアントは全体で15%、ViewControllerを抜いて25%くらい
ビルド、AdHoc配布までの自動化は進んでいる
  • テストはまだなくても、CIサービスを使ってビルド、AdHocでテスター向け配布までは自動化している
  • ブランチモデルにgit-flowを使い、developにマージされるごとにビルド・AdHoc配布
    • スプリントのQA期間など、5〜10/day以上になり、ビルド待ちが発生することも多い
  • CIサービスとしては、Circle CIが多く、次いでTravis CI
    • Circle CIはタスクの並列化が難しいので、例えばテスト実行と平行してAdHoc配布を行なうといったことはTravis CIのほうが向いている
    • Greenhouse CIは、スクリプトを書かずにできる範囲が広く、ほぼスクリプトレスで構築できる。ただし高度なことを行なうには壁がある
    • Jenkinsでもいいけれど、プラグインに頼りすぎずRakefileMakefileに書くように(岸川さん)
    • Rakefileが巨大になったので、XCJobsを作った(岸川さん)
  • AdHoc配布は、Fabric.ioが多い
    • 最近までTestFlightを使い、Fabric.ioの"Beta by Crashlytics"に乗り換えた
      • TestFlightはiOS8以上なのがネック
    • Crashlytics(クラッシュログ解析)を利用していたのでそのまま
    • iOS/Androidで同じサービスのほうが、テストする人はありがたい
    • 弊社はTestFlightがAndroidを切ったタイミングでDeployGateに移ったのですが、もう少し様子見。
  • 自動化にはfastlaneが便利
    • ビルド、テスト実行(xctool)、署名、AdHoc配布などのコマンドは、fastlaneを使うと便利
    • KrauseFx/fastlane · GitHub
  • チャットでコントロール
    • Hubotに"release"を指示すると、リリースブランチを切り、PR、Travisでビルド、配布が流れる
    • チャットにログが残るので、可視化、かつ、作業単位での分担・引き継ぎが容易
Xcodeプロジェクト
  • プロジェクトをcheckoutしたらすぐビルドできるように。プロジェクト固有ルールでの初期設定はなくす。CocoaPodsのようなデファクトを積極的に使う
  • Target、Configurationがたくさんあるとどれを使うべきかわからない。Schemeもたくさんあるとわからない。
  • 依存ライブラリのコードをプロジェクトに混ぜない。CocoaPodsを使えばlockファイルで差分もわかる
    • なお、"混ぜない"は、プロジェクト内で修正を入れたりしないことが重要で、Podsから取得したコードをバージョン管理に含める運用の是非ではないです。
Android
  • Google Playの段階的リリースを使用している
  • 引き継いだ時点でテストコードなし
  • Robotiumを使用
    • リリースビルドをテストできないので、Appiumを検討中
    • まずスモークテスト
    • solo.getView()などをラップして、スクリーンショットを撮るようにする
    • 極力sleep()は使わず、waitForXxxx()を使う
    • ローカライズを意識して文字列はハードコーディングでなく文字列リソースを使う

所感

  • 過剰にUIのテストをしよう!みたいな雰囲気でなくて良かった
  • ビルド・配布から自動化に手を付けるというのは、良い傾向
  • とは言え、あとからテストを書くのは困難なことが多いです。CIを導入しているなら、サイクロマティック複雑度を計測して酷いものからリファクタリングしていくアプローチもあり。『iOSアプリテスト自動化入門』の8.2あたりが参考になるはず

Realm(realm-cocoa)を使うアプリをテストする #realm_jp

iOS/Android向けDBMS+ORMのRealmを利用しているアプリのテストコードの書きかた、またテストコードからIn Memory Storeで使用する方法を試してみました。

realm-cocoa のバージョンは 0.90.6 を使用しています。

テストターゲットの準備

Realmをプロジェクトに取り込む方法はいくつかありますが、今回はCocoaPodsを利用しました。このとき、下記のようにテストターゲット側にもRealm/Headersの利用を宣言する必要があります。

target 'RealmInMemoryStoreExampleTests', exclusive: true do
  pod 'Realm/Headers'
end

テスト用のRealmインスタンスを使用する

Realmのサンプルコードにあるように[RLMRealm defaultRealm]でデフォルトRealmインスタンス*1を使用すると、テストコードからの実行でもテスト対象アプリの利用するDBを使ってしまうため、これに干渉してしまいます。

また、[RMLObject allObjects]といったRealmオブジェクトを指定しないメソッドは総じて、デフォルトRealmインスタンスを使用します。

これを避け、テストコードからはデフォルトとは異なるDBを利用する方法は二通りあります。

方法1: setDefaultRealmPathでデフォルトを置き換える

プロダクトコード(テスト対象アプリ)側で[RLMRealm defaultRealm][RMLObject allObjects]などを直接利用している場合、テストコード側であらかじめ+[RLMRealm setDefaultRealmPath:]を呼ぶことで、以降のデフォルトRealmを置き換えることができます。

通常は[setUp]を下記のようにすれば良いでしょう。

- (void)setUp {
    [super setUp];

    //default realmをテスト専用のものに置き換える(テスト対象アプリのrealmファイルに干渉させないため)
    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *realmPath = [docPath stringByAppendingPathComponent:@"test.realm"];
    [RLMRealm setDefaultRealmPath:realmPath];

    //内容をクリア
    RLMRealm *realm = [RLMRealm defaultRealm];
    [realm beginWriteTransaction];
    [realm deleteAllObjects];
    [realm commitWriteTransaction];
}

これで、以降個々のテストメソッドでは全てのオブジェクトがクリアされたクリーンなtest.realmに対して読み書きを行なうことができます。

なお、DBのクリアは意味的には「後処理」ですが、テストがエラーで落ちる等の要因で実行されないと次(次回)のテストが失敗してしまうこともあります。安定したテスト実行のために[tearDown]でなく「前処理」である[setUp]でクリアを行なっています。

方法2: テスト対象メソッドでRealmインスタンスを受け取る

メソッドの引数、もしくはRealmを扱うクラスのインスタンスフィールドにRealmインスタンスを保持し、これをテストコードから書き換え可能にしておきます。

Realmインスタンスは、デフォルトでなく[RLMRealm realmWithPath:]でファイルパスを指定して初期化できます。 プロダクトコードでは[RLMRealm defaultRealm]を利用し、テストコードからは[RLMRealm realmWithPath:]でファイル名を指定することで専用のRealmインスタンスを使用できます。

プロダクトコード

+ (Employee *)addEmployeeWithName:(NSString *)name salary:(float)salary realm:(RLMRealm *)realm{
    Employee *newEmployee = [[Employee alloc] init];
    newEmployee.name = name;
    newEmployee.salary = salary;
    newEmployee.addDate = [NSDate date];

    //引数で受け取ったRLMRealmインスタンスを使用する
    [realm beginWriteTransaction];
    [realm addObject:newEmployee];
    [realm commitWriteTransaction];

    return newEmployee;
}

テストコード

- (void)testAddEmployee {
    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *realmPath = [docPath stringByAppendingPathComponent:@"test.realm"];
    RLMRealm *realm = [RLMRealm realmWithPath:realmPath];

    //テスト用のRLMRealmインスタンスを渡す
    [EmployeeManager addEmployeeWithName:@"John Cena" salary:2000000 realm:realm];
}

テストデータをファイルで用意して使用する

大量のデータや複雑なクエリをテストするためのデータのinsertは、テストコードで行なうよりも、あらかじめRealmのファイル形式で準備するほうが管理しやすいケースもあるでしょう。*2

これは、上記いずれかの方法でRealmファイルパスを指定してRealmインスタンスを得る場合、あらかじめ用意したRealmファイルをアプリのDocuments下に置き、そのパスを[setDefaultRealmPath:][realmWithPath:]に渡すことで実現できます。

テスト実行環境のファイルの扱いについては、拙著『iOSアプリ テスト自動化入門』の「2.4.1 テストフィクスチャをファイルでバンドルする」「2.4.3 アプリケーションデータを再現してテスト実行する」が参考になるはずです。

In Memory Storeで使用する

Realmは、ファイルでなくメモリ上だけで動作させることもできます。インメモリでの動作は、ファイルI/Oが無いぶん高速、かつ、実行ごとにクリーンな状態のRealmインスタンスを得られるため、テストコードから使うには最適です。

インメモリで動作するRealmインスタンスは、[RLMRealm inMemoryRealmWithIdentifier:]で生成できます。 こちらはデフォルトを置き換えるクラスメソッドは存在しないため、プロダクトコード側が上記「テスト対象メソッドでRealmインスタンスを受け取る」にあるようなRealmインスタンスを差し替え可能な構造である必要があります。

テストコード

- (void)testAddEmployee {
    //インメモリで動作するRealmインスタンスを生成
    RLMRealm *realm = [RLMRealm inMemoryRealmWithIdentifier:@"test"];

    //テスト用のRLMRealmインスタンスを渡す
    [EmployeeManager addEmployeeWithName:@"John Cena" salary:2000000 realm:realm];
}

余り性能は出ないという話でしたが、小さなオブジェクトを1,000件insertする*3テストでは、

  • ファイル : 0.820[sec]
  • インメモリ : 0.189[sec]

となり、1/4程度には抑えられるようです。

サンプルコード

GitHubに置いてあります。

参考資料

Realmのドキュメント

書籍

iOSアプリ テスト自動化入門

iOSアプリ テスト自動化入門

*1:実体はアプリのDocumentsディレクトリ下にdefault.realmというファイル

*2:例えば、RealmファイルはiOS/Androidで同一なので、両プラットフォーム対応の場合には共通化もできます

*3:1レコードごとに毎回commitしています

Realm Tech Talk with JP Simard 1 に行ってきました #realm_jp

iOS/Androidで使用できるDBMS(+ORM)であるRealmのイベント、Realm Tech Talk with JP Simard #1(於 freee株式会社さん)に行ってきました。

Tech Talk with JP Simard from Realm

Realmのメンバー、JP SimardさんによるRealmの紹介。

Realmの特徴

  • オープンソース(ASL 2.0)である。なお、現時点でDBのCore部分はまだクローズドだが、オープンにする方向で進めている。
  • iOS/Androidで同じように使用できる。リリースタイミングのずれはあるが*1、双方、同じ機能を提供する。

既存DBMS(およびORM)との比較

  • iOSのCore DataはSQLiteのORM(Object Relational Mapper)で、古く、遅い。学習コストも高い。
  • SQLiteは早いが、ORMでなくSQLクエリで操作する必要がある。スレッドセーフでない。
  • ほか、FMDB(SQLiteのラッパー)などもあるが、総じて遅い。またAndroid向けには選択肢が少ない。
  • ベンチマーク
    • 単純なinsertは、生のSQLiteが早い(178k件/sec)が、Realmはそれに次ぐ(94k件/sec)。FMDB(47k件/sec)、Core Data(18k件/sec)
    • レコード数カウントは、Realm(30.9回/sec)、SQLite(13.6回/sec)、Core Data(1.0回/sec)。なお、数値はDBに200kレコード入っている状態のクエリ実行回数
    • 抽出クエリは、Realm(31回/sec)、SQLite(14回/sec)、Core Data(2回/sec)

モデルの定義

RLMObjectを継承したValue Objectを定義する。

@interface Employee : RLMObject
@property NSString *name;
@property float salary;
@end

RLM_ARRAY_TYPE(Employee)

@interface Company : RLMObject
@property NSString *name;
@property RLMArray<Employee> *employees
@end

これで、Employeeテーブルと、Employeeの配列(内部的にはrelation)を持ったCompanyテーブルができる。Swiftではデフォルト値の定義も可能。 (コードはその場でメモした擬似コードなので注意)

使いかた

Add(insert)はこんな感じで可能。

let employees = List<Employee>()
let company = Company()

Realm.write{     //transaction
    defaultRealm.add(company);
}

GCDから使う場合

dispatch_async(queue){
    realm.beginTransaction()
    ...
    realm.commitWriteTransaction()
}

(※コードはイメージです)

抽出クエリは、#filter()をチェインしていく形式で記述。

マルチスレッドでRealmを使っても、ちゃんと捌いてくれる。ただし、クエリで取り出したインスタンスはそのスレッドでしか使用できない。別スレッドでは再取得する必要があるので注意。

Notifications

例えばinsertされたタイミングで指定メソッドの呼び出しを登録できる。何がinsertされたかは(今は)わからない。

注意点

  • NSDataは、秒単位までしか保持されない。これはAndroidとの整合性のため。ミリ秒以上の精度はNSTimeintervalを使う。
  • KVOは未サポート

Work In Progress

  • multi process
  • fine-grain notifications(Notificationで操作されたオブジェクトも通知できる)
  • async queries
  • cascading deletes(リレーションのレコードを連鎖で削除する)
  • nullable types(現状はNullを入れられない)
  • sync
  • open source core

質疑応答

理解できたものだけ。

  • Realmのファイルは、iOS/Androidで共通フォーマットなのでファイルコピーで共有できる
  • desktop appで、Realm Browserというのがある。CUIツールは無い
  • coreは、TightDB(旧社名?)
  • in memoryで利用するには、begin transactionのとき指定できる
  • arrayのcountをクエリで取ることはできない(カウントを格納するプロパティを設けるなどのwork around)

realm-javaを使ってみての発表

Chatwork 宮下さんの発表。ChatworkのAndroid版で実際に使用したお話。

Android のORMはそもそも選択肢が少ない。

  • greenDAO: 早いけど準備が面倒
  • ActiveAndroid: insertが遅い(Chatworkさんの事例での話で、大量にinsertが発生すると厳しい)
    • Benchmark: 619件の処理(insert?)で、ActiveAndroidでは1551ms、Realmは414ms。

realm-javaでのモデル定義例

class xxx extends RealmObject{
    @primaryKey private long id;
}
  • RealmObjectはvalue objectでなくてはならず、ロジックは持てない。toString(), equals()とかもオーバーライドできない。
  • Realmのobjectは、スレッド単位でしか利用できない。別スレッドで扱うならqueryしなおす
  • 日本語ドキュメントのバージョンは0.72、最新は0.79.1。かなり変更があるので、最新情報にあたるほうがいい。
  • ButterKnifeと干渉するところがあったが、Gradleの定義で回避できた

所感

iOS/Androidで共用できるシリアライズ機構としては、Dropboxが公開しているmx3も気になっているのですが、こちらはライブラリでなく実装サンプルであり、ORMも含めて考えるとRealmの有望さを感じました。

モデルがvalue objectに限定されることは不便に思いますが、ロジック付きのラッパーを作ればいいので致命的ではなさそう。

最近、DBMSを使う系のアプリを作っていないのですが、止まっている電エースアプリAndroid版ででも使ってみたい。

懇親会では、freeeさんの会社およびプロダクト*2の話をたくさん聞けて、それもまた色々参考になりました。

freeeさん、岸川さん、通訳を買って出てくださった北さん、そしてJPさん、ありがとうございました。

*1:iOSがやや先行するみたい

*2:クラウド会計をユーザとして使いたいと思っていたので

システムテスト自動化カンファレンス2014で「ビルドプロセスとCI」をお話してきました #stac2014

ヤフー株式会社さんで開催された、テスト自動化研究会の旗艦イベント「システムテスト自動化カンファレンス2014」にスタッフとして参加、また、一枠いただいて講演させていただきました。

f:id:nowsprinting:20141215034050p:plain

写真は会場から見下ろせた、見ろ、リアj ミッドタウンのイルミネーション。

システムテスト自動化 標準ガイド

昨年に続き二回目の開催となったシステムテスト自動化カンファレンス2014。今回は、主催のテスト自動化研究会メンバーで翻訳・執筆を行なった書籍『システムテスト自動化 標準ガイド』で得られる知見を中心に紹介する内容でした。

システムテスト自動化 標準ガイド (CodeZine BOOKS)

システムテスト自動化 標準ガイド (CodeZine BOOKS)

  • 作者: Mark Fewster,Dorothy Graham,テスト自動化研究会,伊藤望,玉川紘子,長谷川孝二,きょん,鈴木一裕,太田健一郎,森龍二,近江久美子,永田敦,吉村好廣,板垣真太郎,浦山さつき,井芹洋輝,松木晋祐,長田学,早川隆治
  • 出版社/メーカー: 翔泳社
  • 発売日: 2014/12/16
  • メディア: 大型本
  • この商品を含むブログを見る

翔泳社様による書籍の先行販売も行われ、ご用意いただいた分および、急遽取りに行っていただいた会社在庫分まで完売してしまったとのこと。購入できなかった方は申し訳ないです。

またどこからか「ギア本」という愛称も付けていただいたようで、翻訳・執筆陣の端くれとして有難い限りです。

システムテスト自動化カンファレンス2014

カンファレンスの内容、また他の登壇者の資料については、connpassやテスト自動化研究会のWebサイトなどに順次上がっていくはずですので、そちらを参照してください。

ビルドプロセスとCI

システムテスト自動化 標準ガイド』の第14章「CI(継続的インテグレーション)」を書かせていただいた流れで、CIやビルドパイプラインまわりについてお話させていただきました。

会場は、何らかのCIツールを使っている方が7割くらい。Travis CIなどのサービスを使っている方は5〜6名ほど。

余りテクニカルな話ではなく、ツール起点で考えていませんか?基本に立ち返るために先人の知見に立ち返ってみては?という話。 「言われてみれば当たり前」のことに気付かないことは往々にしてあるはずで、それをちょっと視点を変えて見てみるきっかけにでもなれば幸いです。

実はかなり直前までオチがつかず困っていたのですが*1、「繰り返しはギャグの基本」という言葉を思い出してまとめてみました。基本大事。

なお、スライド内でも紹介していますが、CIやビルドパイプラインまわりのテクニカルな話は『継続的デリバリー』を読むのが一番だと思います。 『継続的インテグレーション入門』はさらに深く、原点に当たりたい人は読むといいと思います。

継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化

継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化

継続的インテグレーション入門

継続的インテグレーション入門

以下、質疑応答でいただいた内容とかTweet拾ったものとか。

Jenkinsについて

私のオススメは「かえる本」

Jenkins

Jenkins

あとは用途(目的)に応じて、という感じでしょうか。

Walterについて

Jenkinsのプラグインに対するアドバンテージを質問されました。私としては、

という点です。Jenkinsでもジョブ設定の差分を管理できるJobConfigHistory Pluginがありますが、可用性が断然違ってきます。

また、方向性としては、私の後に講演されたYahoo! JAPAN赤根さんのように、ジョブ自体を毎回生成するアプローチもありますね。

Walterについては、作者さんのブログ記事もご参考。

シンプルなビルドパイプラインツールwalterをリリースしました | Advanced Technology Lab

Travis CIについて

口頭で挙げたもの。

iOSアプリのビルド

はい、忘れてました。CloudBeesでもMac OS XのSlaveを選択できるので、iOSアプリのビルドできるはずです。少し前にPriceとか変更になったのですが、以前は有料プランでのみ選択可能だったはず。

【12/18追記】CircleCIもiOSビルドに対応したと発表されました。 see: Announcing the Launch of iOS and Android Support on CircleCI | The Circle Blog

iOSMac OS Xアプリのビルドは、Mac OS Xで実行する必要があります。JenkinsをLinux上に立てている場合はSlaveとしてMacを接続し、そこでビルドが走るようにする必要があります。

上記のTravis CIなどは、Mac OS Xの動作するWorkerを備えています。このような形でMac OS Xを使うのはAppleと別途契約しているという話だったはず。

iOSアプリのビルド自動化については、こちらがおすすめです(自薦)。

iOSアプリ テスト自動化入門

iOSアプリ テスト自動化入門

*1:中だるみについては早々に諦めた

Cardboardでもユニティちゃんを表示させてみた

Oculus Rift DK2で表示させたユニティちゃんを、できるだけそのままCardboardTaoVisor立体視してみます。

開発環境などは先のエントリを参照。

Durovis Dive Plugin

Durovis DiveというCardboardクローン的プロダクトがあり、そこでiOS/Android向けに簡単に立体視と視点のトラッキングを実現できるUnityプラグインが配布されています。

以下の手順で適用します。

  1. Dive SDKのページからプラグインをダウンロード(執筆時点のバージョンは2.0 r498)
  2. Assets -> Import Package -> Custom package...でインポート
  3. Project ViewのAssets/Dive/Dive_CameraをHierarchy Viewにドラッグアンドドロップ。従来のカメラを無効化(Inspectorでチェックをoff)
  4. Dive_CameraのTransformを適切に設定(Oculus Riftのときと同じ)
  5. スプラッシュスクリーンの表示が必須とのことなので、AssetsにあるDive-Splashscreen.unityをビルド対象に追加(File -> Build Settings...の"Scenes In Build"に追加)

f:id:nowsprinting:20141103153246p:plain

Oculus Riftに比べて視野角が狭いので、外周の歪みも無い(無くていい)みたい。

Androidデバイス向けにビルドする

Androidデバイス向けのビルドを行ないます。

なお、動作を試すだけなら、Unity Remote 4が使えるはずなのですが、Android版とUnity 4.5.5では同期できませんでした。

ビルドの設定

  1. File -> Build Settings... を開き、Platformの"Android"を選択します
    • ここで"Google Android Project"をonにすると、apkでなくAndroidプロジェクト(Ant版)が出力されます
  2. Build Settingsの[Player Settings...]ボタンをクリックするとInspectorの表示が切り替わり、ここでアイコンなどを設定できます
    • "Other Settings"に、"Bundle Identifier"、"Bundle Version"および"Bundle Version Code"の設定があります。"Bundle Identifier"の設定は必須です
    • "Publishing Settings"に、apkに、署名するためのキーストアの設定があります。デバッグ証明書を使う場合はキーストア未選択のままでいいようです
  3. Unity -> Preferences... -> External Tools の"Android SDK Location"を設定(選択)します
  4. これで[Build]もしくは[Build and Run]ボタンでビルドできます(出力ファイルパスを聞かれます)

コマンドラインでビルド

AndroidアプリはUnityコマンドで直接ビルドできないため、Unityプロジェクト内にビルドを実行するスタティックメソッドを作成し、それをコマンドラインから呼び出します。

ビルドメソッドの作成

まず、スクリプトの格納フォルダを作ります。Assets -> Create -> Folderでフォルダを生成。名前をEditorに変更します(namespace UnityEditorをエラーにしないために必須)。

続いて、Project ViewのEditorフォルダを右クリック -> Create -> C# Scriptでスクリプトを生成します。名前は任意ですが、クラス名と一致させる必要があるのでここではBuilder.csとしました。

using UnityEngine;
using UnityEditor;

public class Builder {

    static void BuildAndroid(){
        string[] scenes = {
            "Assets/Dive/Dive-Splashscreen.unity",
            "Assets/UnityChan/Scenes/UnityChan_hw.unity"};

        string msg = BuildPipeline.BuildPlayer(
            scenes,
            "Build/UnityChanVR.apk",
            BuildTarget.Android,
            BuildOptions.None);

        if(!string.IsNullOrEmpty(msg)){
            throw new System.Exception(msg);
        }
        Debug.Log("Build completed ");
    }
}

失敗時は例外をスローすることで、Unityコマンドが終了コード1で終了します。

コマンドライン指定

次のコマンドで、BuilderクラスのBuildAndroid()メソッドを実行できます。

$ /Applications/Unity/Unity.app/Contents/MacOS/Unity -quit -batchmode -logFile /dev/stdout \
    -executeMethod Builder.BuildAndroid

なお、

  • Unityは二重起動できないため、コマンド実行時Unityは落としておく必要があります(MonoEditorは起動していてok)
  • 出力ファイルは上書きしてくれますが、必要なディレクトリ(上例ではBuildディレクトリ)はあらかじめ掘っておきます
  • -logFile /dev/stdoutを指定することで、通常コンソールに出力されるログを標準出力に出力できます

iOSデバイス向けにビルドする

iOSデバイス向けのビルド(正確には、iOS向けのXcodeプロジェクトの出力)を行ないます。

なお、動作を試すだけなら、Unity Remote 4を使うと便利です。

OpenDiveSensor.csの修正

そのままiOS向けビルドを行なうと、Assets/Dive/OpenDiveSensor.csでAndroidJavaObjectが見つからないというエラーが出ます。このオブジェクト宣言自体が不要なので*1以下は削除します。

AndroidJavaObject mConfig;
AndroidJavaObject mWindowManager;

Oculusパッケージをプロジェクトから取り除く

OculusUnityIntegration.unitypackageをインポートしている場合、それがシーンで使用されていなくてもビルドエラー*2が出るようです。iOSデバイス向けでは使用しないので、Project ViewのAssets/OVRを削除します。

ビルドの設定

  1. File -> Build Settings... を開き、Platformの"iOS"を選択します
    • "Symlink Unity libraries"は、UnityのライブラリをXcodeプロジェクトにコピーしないため高速に動作します。XcodeプロジェクトだけCIサーバなどでビルドするのでなければonでいいはず
  2. Build Settingsの[Player Settings...]ボタンをクリックするとInspectorの表示が切り替わり、ここでアイコンなどを設定できます
    • "Other Settings"に、"Bundle Identifier"と"Bundle Version"の設定があります
    • "Bundle Identifier"は、Info.plistのCFBundleIdentifierにセットされますが、末尾は${PRODUCT_NAME}に置き換えられます
    • "Bundle Version"は、Info.plistのCFBundleShortVersionStringCFBundleVersionの両方にセットされます
  3. これで[Build]もしくは[Build and Run]ボタンでXcodeプロジェクトを出力できます(出力ファイルパスを聞かれます)

コマンドラインでビルド

iOSアプリ(Xcodeプロジェクト)もUnityコマンドで直接ビルドできないため、Unityプロジェクト内にビルドを実行するスタティックメソッドを作成し、それをコマンドラインから呼び出します。

ビルドメソッドの作成

Android向けに作成したBuilderクラスに、BuildIos()メソッドを追加します。

using UnityEngine;
using UnityEditor;

public class Builder {

    static void BuildAndroid(){
        (snip)
    }

    static void BuildIos(){
        string[] scenes = {
            "Assets/Dive/Dive-Splashscreen.unity",
            "Assets/UnityChan/Scenes/UnityChan_hw.unity"};

        string msg = BuildPipeline.BuildPlayer(
            scenes,
            "Build/UnityChanVRiOS",
            BuildTarget.iPhone,
            BuildOptions.SymlinkLibraries);

        if(!string.IsNullOrEmpty(msg)){
            throw new System.Exception(msg);
        }
        Debug.Log("Build completed ");
    }
}

コマンドライン指定

次のコマンドで実行します。

$ /Applications/Unity/Unity.app/Contents/MacOS/Unity -quit -batchmode -logFile /dev/stdout \
    -executeMethod Builder.BuildIos

Xcodeプロジェクトのビルド

生成されたプロジェクトは、Xcodeでビルドできます(Xcode 6.0.1で確認)。 Xcodeプロジェクトをコマンドラインでビルドするにはxcodebuildコマンドを利用できます。詳しくは『iOSアプリ テスト自動化入門』のChapter 6などを参照してください。

iOSアプリ テスト自動化入門

iOSアプリ テスト自動化入門

参考

Unity実践技術大全 (GAME DEVELOPER BOOKS)

Unity実践技術大全 (GAME DEVELOPER BOOKS)

ライセンス

ユニティちゃんライセンス

このコンテンツは、『ユニティちゃんライセンス』で提供されています

*1:Android向けに必要な物は"#if UNITY_ANDROID"の中に書かれているので

*2:Cross compilation job Assembly-CSharp.dll failed.