My Favorite Things - Coding or die.

とある技術者の経験記録、的な。

Quick/Nimbleで独自Matcherを自作して、テストコードの可読性を上げる

さて今年も終わりに近づいてきました。

この記事はモバイル 自動化 / 自動テスト Advent Calendar 2017の17日目の記事です。

実はアドベントカレンダーの参加は今回が初めてだったりするのですが、まぁそれはどうでも良いですよね。

Tl;Dr

Quick/Nimbleで、独自Macherを作成するとテストコードの可読性が上がるかも?

// 標準のMatcherを使用
it("use built-in matcher") {
    expect(person1.isTeen()).to(beTrue())
    expect(person2.isTeen()).to(beFalse())
    expect(person1.gender).to(equal(person2.gender))
    expect(person1.gender).toNot(equal(person3.gender))
}

// 独自のMatcherを使用
it("use custom matcher") {
    expect(person2).to(teen())
    expect(person1).toNot(teen())
    expect(person1).to(sameGender(person2))
    expect(person1).toNot(sameGender(person3))
}

iOS Test Night

さて、iOS Test Night #6 - 1周年 - では、Quick/Nimbleのイケてないところを改善したい、といった内容で発表をさせていただきました。 speakerdeck.com

その中で「Matcher APIはコード補完が効きづらいのがイケてない」的な話をしました。

たしかにMatcher APIはコード補完が効きづらく、初心者にとっては学習コストが高いものです。しかし、Matcher APIが悪いかというとそうではなく、あえて柔軟に作られるように設計されています。

そこで今回は、独自のMatcherを作成する方法と、それによってテストコードがどのように変わるのか見ていきたいと思います。

Quick?

QuickはSwift製のBDDフレームワーク(+Matcher APIベースのAssertionライブラリ)です。

私の記憶では、Swiftがリリースされてから2日後くらいには公開されていた、かなり早くからあるテスティングフレームワークです。Ruby製のBDDフレームワークであるRSpecなどにインスパイアされていると、公式のREADMEで書かれています。

BDDフレームワークとは「振る舞い(Behavior)」に着目してテストコードを書こうという思想のテスティングフレームワークです。

歴史的には、先にTDDによる「テスト駆動」という「テストを先に書く」という思想を開発にもたらされました。

しかし、「テストを書くこと」が目的になるという悪い側面もあり、それを解決するために(テスト対象の)「振る舞い」に着目することで質の良いテストを書こう、ということでBDDが生まれたとかどうとか。(そんな話を聞いた覚えがあるような、くらいなので間違っているかも)

以下のようにテストコードが(DSLによって)構造化されるのが最大の特徴になっています。

class SampleTest: QuickSpec {
    override func spec() {
        describe("足し算") {
            context("1 + 1") {
                it("2") {
                    expect(1 + 1).to(equal(2))
                }
            }
            context("1 + 2") {
                it("3") {
                    expect(1 + 2).to(equal(3))
                }
            }
        }
    }
}

よくありがちな単純な例ですが、テストが構造化されるのが見て取れるかと思います。

コード中に出てくる単語は、以下のような意味を持っています。

  • describe:テストの概要説明
  • context:テストの条件
  • it:期待される振る舞い

このような形で、テストコード全体が構造化されるのがBDDフレームワークの特徴になっており、さまざまな言語でBDDフレームワークが作成されていますが、この見た目に関してはだいたい似たような感じになっています。

Nimble?

Quick/Nimbleとセットで呼ばれる事が多く、実際セットで使われることが多いため、あまり意識されることは多くない(ように思える)のですが、Nimbleは「Assertionライブラリー」という位置付けになっています。

QuickがBDDフレームワークとしてDSLを提供するのに対し、NimbleはXCTestでいうところのXCTAssertEqualのような期待値と結果を比較して、テストの成否を判定する機能を提供しています。

さきほどのコード中にexpect(1 + 1).to(equal(2))といったコードがありましたが、この部分はQuickではなくNimbleに用意されたAPIを利用しています。テストコードが自然な英文になるようなAPIが提供されており、「Matcher API」と呼ばれることが多いです。

Matcher APIのメリットは、テストコードが自然な英文になることで意図が分かりやすくなることと、失敗時のエラーメッセージが分かりやすいという点が挙げられます。

expect(1 + 1).to(equal(3))
// => expected to equal <3>, got <2>

この失敗時のエラーメッセージが分かりやすいというのは意外と重要で、プロダクトコードに何らかの変更を加えた時に既存のテストが失敗した場合は、その原因をできるだけ早く知りたいと思うはずです。失敗時のエラーメッセージが不親切だと、なぜテストが失敗したかの原因を調べるのに多大な時間を消費してしまいます。

これはミクロな視点で見た時は大した問題にならないように見えますが、マクロな視点で見ると結構重要だったりします。

CI(継続的インテグレーション)により自動テストが失敗した場合に、その原因がすぐに分からない場合は(他の作業との兼ね合いで)修正が後回しにされることが多いように感じます。それが繰り返されると、テストコードのメンテナンスがされなくなり、最後には捨てられるということも少なくありません。(実際に、私も以前関わったPJで目の当たりにしています)

Matcher APIの欠点

Matcher APIは以下を提供することで、前述したような問題への対処を試みています。

  • テストコードの意図を明確にする
  • テストが失敗したときの原因を明確にする

一方で、IDEによるコード補完との相性は悪く、初心者にとっては学習コストが高めです。

その対処として、

といった改善策を考えた、というのが冒頭のiOS Test Nightでの発表内容になります。(詳しくはスライドをご参照ください)

独自Matcherを自作する

ようやくタイトル回収までたどり着きました。

Matcher APIの他のメリットとして、独自のMatcherを作成することが出来るという点が挙げられます。つまり、Built-inのAPIで十分な可読性が得られなければ、自分で拡張することもできるという意味です。

今回は以下のPerson構造体に対して、「ティーンエイジャー(13〜19歳の間)であること」と「性別が同じであること」というMatcherを作成してみたいと思います。

struct Person {
    
    enum Gender {
        case male
        case female
    }
    
    let age: Int
    let gender: Gender
    
    func isTeen() -> Bool {
        return 13...19 ~= age
    }
}

期待値を受け取らないMatcher - teen()

まずは「ティーンエイジャー(13〜19歳の間)であること」を確認するテストコードについてです。

標準のMatcherを利用したテストコードを見てみます。

let person1 = Person(age: 17, gender: .male)
let person2 = Person(age: 20, gender: .male)

describe("Person") {
    
    describe("is teen?") {

        it("use built-in matcher") {
            expect(person1.isTeen()).to(beTrue())
            expect(person2.isTeen()).to(beFalse())
        }
    }
}

悪くありませんが、isTeen()の呼び出し結果がtrue/falseであること、といった感じで少しだけ回りくどいテストコードのようにも見えます。

次に自作したteen()というMacherを利用したコードを見てみます。

let person1 = Person(age: 17, gender: .male)
let person2 = Person(age: 20, gender: .male)

describe("Person") {
    
    describe("is teen?") {

        it("use custom matcher") {
            expect(person1).to(teen())
            expect(person2).toNot(teen())
        }

    }
}

さきほどの標準APIに比べて、意図が分かりやすくなったのではないでしょうか?

以下がMatcherの実装です。

func teen() -> Predicate<Person> {
    return Predicate { (actualExpression: Expression<Person>) throws -> PredicateResult in
        let message = ExpectationMessage.expectedTo("teenager")
        if let actualValue = try actualExpression.evaluate() {
            return PredicateResult(
                bool: actualValue.isTeen(),
                message: message
            )
        } else {
            return PredicateResult(
                status: .fail,
                message: message
            )
        }
    }
}

ゴチャゴチャしているように見えますが、だいたい決まりきったコードパターンになっているので、公式のREADMEに書かれたサンプルコードを真似すればわりと簡単に作成できます。(実際、内部的な実装までは把握していません)

beTrue()のように期待値を受け取らないパターンは、このような感じのコードで実装することが出来ます。

期待値を受け取るMatcher - ()

次に「性別が同じであること」を確認するテストコードについてです。

同じように標準APIのコードを見てみます。

let person1 = Person(age: 17, gender: .male)
let person2 = Person(age: 20, gender: .male)
let person3 = Person(age: 15, gender: .female)

describe("same gender") {
    
    it("use built-in matcher") {
        expect(person1.gender).to(equal(person2.gender))
        expect(person1.gender).toNot(equal(person3.gender))
    }
}

やはり悪くはありませんが、視覚ノイズが多く、パット見で意図を読み取るのが難しくなっている印象を受けます。

次に自作したsameGender()によるテストコードです。

let person1 = Person(age: 17, gender: .male)
let person2 = Person(age: 20, gender: .male)
let person3 = Person(age: 15, gender: .female)

describe("same gender") {
    
    it("use custom matcher") {
        expect(person1).to(sameGender(person2))
        expect(person1).toNot(sameGender(person3))
    }
}

先程の標準APIに比べ、はるかにテストの意図が分かりやすくなったのではないでしょうか?

sameGender()の実装は以下のとおりです。

func sameGender(_ expectedValue: Person) -> Predicate<Person> {
    return Predicate { (actualExpression: Expression<Person>) throws -> PredicateResult in
        let message = ExpectationMessage.expectedActualValueTo("same gender <\(expectedValue)>")
        if let actualValue = try actualExpression.evaluate() {
            return PredicateResult(
                bool: actualValue.gender == expectedValue.gender,
                message: message
            )
        } else {
            return PredicateResult(
                status: PredicateStatus.fail,
                message: message
            )
        }
    }
}

引数として期待結果expectedValueを受け取るようにしているという違いはありますが、基本的には先程と同じようなコードになっています。

いつMatcherを作成すべきか?

これについて明確な答えはありません、おそらくプロジェクトによってマチマチかと思います。

独自Matcherを作成したほうが読みやすくなるからと言って、片っ端から独自Matcherを作成していたのでは、そちらのコードの記述量が多くなってしまって逆にコストが掛かるでしょう。

それにテストが失敗した時に、独自Matcherの不具合ではないかと疑いをかけたくなるケースもあるでしょう。そういう意味では独自Matcherもきちんとテストされるべきかもしれません。

しかしアプリケーションの中心となるドメインモデルがあり、独自Matcherを作成することでテストコードの可読性があげられるのであれば、独自Matcherを作成することを検討する価値はあるでしょう。

テストコードを負債にしないために

最近わたし自身が感じているのは、テストコードが失敗したときの原因が「ほぼ一瞬」で分からない場合、「面倒だからあとで調べよう」となるという気持ちのコンテキストスイッチの切り替えが心の中で起きるということです。

それは「実際に調べてみたら大した事がなかった」としても、「対応までの時間と(心理的な面も含めて)コストが掛かる」ということです。テストコードが負債になっていくのは、失敗したテストコードに対するチームメンバーの心理的ストレスではないかと思うわけです。

そういう意味で独自Matcherの作成は、適材適所で利用すれば価値があるのでは、と思ったりします。

終わり

最初は独自Matcherの作成という技術的な記事にするつもりだったのですが、なんだか途中からポエミーな感じになってしまいました。まぁ、年の瀬ですし、たまにはこういうのも良いでしょうか。

個人的に次の課題として、NimbleのMatcher APIまわりの実装の仕組みをきちんと理解したいと思うので、今度コードリーディングしてみたいなと思ったりします。

そんなわけでモバイル 自動化 / 自動テスト Advent Calendar 2017の17日目の記事でした。

皆様良いお年を。(ちょっと早い?)

勉強会:Bonfire iOS #3 に参加してきた。

さて、気づけばどうやら久しぶりのブログ更新のようです。

今回は、Bonfire iOS #3 というヤフーが開催しているIOS系の勉強会に参加してきました。 yj-meetup.connpass.com

第3回の今回のテーマは「品質」ということで、様々な会社・プロダクトに携わっている方たちが品質について発表してくださいました。

iOSの勉強会というと、開発Tipsやテストといった技術的なテーマが多い中、面白いテーマだなぁと思いました。

いつものように箇条書きメモ+感想みたいな感じで順番に書いていきたいと思います。 スライドがアップされたら更新していきたいと思います。

Yahoo!天気アプリを少人数でも支えられた理由

  • 自己紹介
    • iOS歴3年
    • Swift
    • 大阪から
  • Y天気アプリ
    • 利用者あわせて600万
    • 天候があれると1000万
    • 統合ランキングでも1位をなんども
  • 体制
    • 17人くらい
    • iOS/Android、1名ずつ
    • 他の社内サービスでも珍しい
  • 企画
    • メンバーと新機能を決める
    • PMもエンジニア経験ある
      • 企画が実装も踏まえて話を進めてくれる
    • Sckethでエンジニアがプロと
    • 職種の垣根をこえて進められる
  • 開発フェーズ
    • 最初から実機で進めている
    • シミュレータでは感じられない操作感などがわかる
    • デザイナーに見てもらうときも、いろいろな実機をもっていく
      • ヘルプデスクから端末を借りられる
  • テストフェーズ
      1. 社内自動ビルドシステムを利用
      2. 社員はみんなiPhoneを持ってる
      3. Before/Afterをチームメンバーのために作っている
      1. 前バージョンからの変更をコードで見る
      2. PRとは別に差分をみて変更を把握する
      1. QAによるTestFlightを利用したテストケース
      2. 大きな差分がある場合は外部に依頼することも
        • 端末の網羅性など
        • お金がかかるので不安が消えないときなど
      1. iTCのプロモーションコードによるリリース直前チェック
      2. 審査が通ったあとも最終確認
      3. アップデート時にマイグレーションで落ちることも
      4. クラッシュレポートなどを確認
        • 浸透するには4日間くらい
      5. アップデートの評判を聞く
      6. 一部はアンケートをとったり
  • 天気アプリならでは
    • 天気情報を見せるのがメイン
    • 入力などはないのでそのあたりのセキュリティなどの考慮はあまりない
    • プラットフォームが整っている
    • 生活の一部
      • 毎日起動されるアプリなので、見た目の品質が重要
      • ユーザは細かいことでも気になる
  • コンテンツの品質
    • 災害発生時の動き
      • 8面モニタで情報収集をしている
        • 災害発生時にすぐに分かるように
    • サービスマネージャが天気予報士の資格を持ってる
      • 予想しながら動くことも
    • アプリチームは?
      • ユーザが必要そうな情報は発信
      • 手動で通知をうって、ユーザに知らせる
        • チャット上で通知の文言を相談する
        • 事実を提供する
      • 災害時
        • 一部はWebViewなのでデザイナが更新できる
        • 災害が落ち着いてから情報が十分だったか振り返り
    • ピンチのときにユーザを助けてこそ会社の信頼につながる
  • まとめ
    • 少人数PJを支える強力な社内システム
    • 見た目に徹底
    • MixLeap
      • 大阪2拠点になります
      • 毎週イベントを開催している
      • フォロー歓迎(Twitterアカウント)

私も普段から利用している「Y!天気」アプリの発表でした。

まず、iOS/Androidエンジニアが1名ずつという点に驚きました。そういった少人数でも品質を保つ大勢や仕組みが非常に参考になりました。

参考になることばかりだったのですが、あえてピックアップするとすれば以下が特に参考になりました。

  • 前バージョンからのコード差分を見る
  • 毎日起動されるアプリなので、細かいUIについても拘る
  • サービスマネージャが天気予報士の資格を持っている

当たり前な品質を支える様々な仕組みと、天気というサービスに特化した品質の考え方が取り入れられていて、とても戦略的にサービスを開発・運用しているのだと強く感じました。

iOS アプリエラー監視のための設計と効果

speakerdeck.com

  • 自己紹介
    • 開発速度を2倍にした
    • 趣味はTODO
  • エラー監視とは?
    • アプリで発生したエラーをサーバに送る
    • クラッシュレポート
    • Crashlyticsなど
  • クラッシュ以外のエラーも監視できる
    • 内部状態の不整合とかも
  • エラー監視の特性
    • 広範なバグを検知
    • 発見が遅い
  • バグが埋め込まれてからの時間が短いほうが嬉しい
    • 特殊ケースのバグは発見が遅くなる
    • 下から2つ目
      • 最後の砦
      • ユーザからの問い合わせはあるいみNG
  • 理想のバグ検知
  • ダメ検知
    • 発見までの時間が長くなる
    • エラー監視は特殊なバグを検知する手段
      • それ以外はそれより前のフェーズで見つけるべき
    1. エラー監視サービスのAPIを把握
    2. Crashlytics以外でもAPIドキュメントを探そう
    1. エラーレポーターをカスタマイズ
    2. NSErrorではなくSwiftのErrorで送信できるように
    3. 発生元のオブジェクトを渡す
    4. DEBUGと本番を分ける
      • DEBUGでも送信したほうがベター
      • 区別できれば良いのではないか?
    5. NSErrorへの変換
      • domain/codeで設定される
      • 発生元をreporterとしてdomainに含める
      • エラーメッセージを含めてはいけない
        • 同じ不具合が別のバグとして分類されてしまう
  • 実際に組み込む
    • ハードコード
      • 単一責務原則に違反する
      • 単体テストでエラーレポートが送信されてしまう
      • isTestフラグ
    • Observerパターン
      • エラーReporterを監視者として、エラーが発生したら送信
      • テストの時にはエラーレポーターを使わない
      • Observerパターンを使える場所は限られる
        • Modelはだいたい監視できる作りになっているはず
      • RxのObservableを使っている
    • エラー監視にはObserverパターンが適している
  • 困ったケース
    • Fatal Exception
    • エラーにはなるべくクリティカルな情報を含める
    • 原因究明をわかりやすくする
      • nilを返すのではなくenumを返す
  • 結果
    • ユーザの手元で発生しているバグの種類や規模を把握できるようになった
      • 重要度のジャッジが出来るようになった
    • ユーザの手元では予想外のエラーが起こっていることに気づく
      • 特定のキャリア回線で発生する不具合など
      • Sandboxのレシートじゃないと再現しないとか
    • 副作用
      • テスターの動作確認中にエラーレポートが飛んでくる
  • まとめ
    • エラー監視で、テスターが発見できない不具合が発見できる
  • 宣伝
    • ランチ募集

モバイルアプリの開発において、避けては通れないエラー監視をどのように扱うか、またどのように設計したら良いかという発表でした。

Twitterのタイムラインでも話題になっていましたが、エラーの種類とそれがどこで発見されるべきかという図がとても分かりやすかったです。こういったことは開発が忙しくなると特に忘れがちなので、こういった図にまとめられているととても良いと思いました。(チームメンバーが見える位置に貼っておくというのもありかと思いました)

そしてエラー監視の設計についてはObserverパターンを利用すると良い、という話が後半でした。Observerパターンを採用することで、単一責務の原則を守ることが出来て、かつ不要なときは観測者(Observer)を外せば良い、という考え方はよくできているなぁと思いました。

あと、エラーレポートに詳細なメッセージを含めない、DEBUG環境でも区別できるようにした上で送信してしまう、というのも参考になりました。(私が以前関わっていたiOSプロジェクトでは、DEBUG時は送信しない設定にしていました)

スタートアップでのQA

  • 自己紹介
    • QAエンジニア10年
    • Six Apart
    • TORETA
      • 飲食店向けの顧客サービス
      • iPadでスケジュールを管理する
        • 顧客データがたまるメリットも
        • 集計・分析にも利用できる
        • ウェブ予約との連携も
        • 一元管理できるのがメリット
  • QA?
    • 会社や組織によってまちまち
    • 入社時の肩書として、QAエンジニアを選んだ
      • プロダクトの品質を広範囲でサポートしたい
      • テストは品質保証の一手段
  • 2年間でやったこと
      1. 顧客を知る
      2. 実際になにが大変で、どういった使われ方をするのか
      3. 営業同行+新機能のヒアリング
      4. データから利用状況を知る
        • BigQuery
      5. バグのトリアージ
      1. 価値の高いテストに集中する
      2. テストに限られるリソースは限られている
      3. 関係者を巻き込む
        • セールス・サポート:おさわり会
      4. E2Eテストは同じリポジトリで管理
        • QAエンジニア以外でもテストコードをかけるように
      1. 自動化
      2. 時間ができたら自動化に取り組む
      3. テスト・デプロイの自動化
      1. 開発・運用の持続可能性を高める
      2. TORETAはプロダクトのライフサイクルが長い
        • 5年・10年、それ以上も?
      3. ドキュメント
        • 書くのもメンテするのもかなりのコスト
        • 書きすぎないを意識(問い合わせがあったら書く、くらいの感覚)
      4. 手動テストケースを追加した理由を残す
        • 長く時間が経つと、なぜ追加したのかわからなくなることも
        • Markdownで書いて履歴として管理する
      5. 長くプロダクトを育てていくために
      1. 製品の課題をサポートで補完する
      2. B2Bなので顧客に直接連絡できる
      3. サポートチーム経由で店舗にヒアリングできる
      4. サポート対象バージョンを絞る
        • セールス経由で店舗側でアップデートしてもらうことも出来る
      1. サポート運用の課題を技術で解決する
      2. サポートの意見を製品に活かす
        • サポートチーム、アカウント管理チームと週1ミーティング
        • お知らせ機能
        • 問い合わせ番号
        • 設定画面の文言改善
  • まとめ
    • スタートアップは総力戦(みんなで協力する)
    • 顧客のことをよく知る
    • 価値が高いことに集中する
    • プロダクトの問題をプロダクトだけで改善しない
      • プロダクト品質 x サポート品質
    • お隣さんとはアイディアが眠っている

飲食店向けのiPadで予約管理できるサービスにおいて、QAエンジニアとしてどう関わったかという発表でした。QAエンジニア歴10年ということで、参考になる話ばかりでした。

個人的には、顧客やプロダクト、サービスの性質(ライフサイクルが長い、B2B)を考えた上での取り組みが行われていることが、とても素晴らしいと感じました。とくに「顧客のことをよく知る」というのは品質を考えていく上でとても大切なことだとあらためて思いました。

ちなみにテストケースをMarkdownでバージョン管理するというやり方は、以前取り組んだものの失敗した経験があるので、機会があればもう一度挑戦してみたいなと思いました。

QA組織とiOSのテスト

speakerdeck.com

  • 自己紹介
    • LINE株式会社
    • サービスQAチーム
    • LINEファミリーアプリを担当
    • QAエンジニア歴9年
    • 社内インタビュー
  • QAエンジニアのロール
    • QA?
      • テスト管理全般をする
      • 上流からどういうテストが必要可考えて、プロダクトの品質を評価
      • SET
        • テスト自動化
      • Tester
        • 手動テスト
    • QAエンジニアに必要なスキル
      • 幅広いスキルセットが必要
    • チームにQAとしてアサイ
    • 静的テスト
      • インスペクション(仕様レビュー)
      • 欠陥の防止
    • 動的テスト
  • 品質評価の考え方
    • リリース判定の考え方
    • Webアプリとは異なり、ロールバックが即座にできない
    • 品質の推移を測定
      • メトリクスを取る
    • 信頼度成長曲線
      • リリースにはバグカーブが収束していること
    • スクリプトテストの予実管理
      • すべてのテストがパスすること
    • メトリクスの落とし穴
      • 不具合が収束していないからと言って品質が不十分であるとは限らない
    • 品質モデル
    • テストと開発者は協業したい
    • QAはできるだけ開発が開発に集中できるように
    • 開発の品質に対する不安をなくしたい
  • 取り組み
    • テストケースの見える化
    • テスト実行結果
    • テストケースは誰でも見えるように
  • テスト結果とBugを紐付ける
    • どのテストによってBugが生まれたか開発者がわかりやすい
  • グレーボックステストのアプローチ
    • 課題
      • BTSを見ただけでは何を修正すればいいかわからない 
      • バグレポートの制度を上げる
      • テスターにもAPI Referenceを読んでもらう
  • iOS固有のプラットフォーム
    • テストしている側がプラットフォームを理解していない
    • テスターもヒューマンインターフェースガイドラインを読む

QA組織としてどのようにプロダクトの品質を判定、リリースしているかについての発表でした。

BTSなどのメトリクスをきちんと測った上で、QCDとのバランスを考えた上でリリース判定しているというのが非常に良いと思いました。

私は以前とあるプロジェクトで開発していた時にQA組織がメトリクスを測っていたのですが、とてもレアケースな不具合が(一般的に想定される利用ケースではまず発生しないようなもの)多くあったことを理由に品質が不十分であると判定された経験がありメトリクスについては懐疑的でした。(QA組織はきちんと仕事をしてくださった、とも言えますが)

今回の発表ではメトリクスを測った上で、それだけを正解(あるいは正義)にしていないのが個人的にはとても好印象でした。

あと品質の見える化を行うというのは、とても良い試みであると感じました。

SmartNewsアプリの品質

www.slideshare.net - 自己紹介 - 荒巻 賢一 - 趣味:いろいろ - SmartNewsの品質 - ニュースコンテンツへのこだわり - アルゴリズムを改善し続ける - 広告へのこだわり - 広告もコンテンツの一部である - ピクセル単位で調整したり - 開発 - ブランチ:git-flow - pushでXcode Serverでビルド&テスト - prototypeでベータ配布 - スプリント - iOS/Androidで2週間単位 - QAは1週間単位で交互にテスト - iOSの標準に従いつつ必要なら変更 - けい素解析で折り返し - なめらかなページめくり - BTS - Apple Developer Technical Support事例 - iOS 9.0のみでクラッシュ - WKWebViewのキャッシュがたまり続ける - アプリ実装のこだわり - 星マークの歪み - AppStoreでは正方形に内接(星としては歪み) - クリスさんより(XCUITest) - ゴールデン街に詳しい人はぜひ - ビルドサーバの構成 - サーバは2台 - Productionビルド用 - Testビルド用 - Xcode Serverの次バージョンを確認 - 問題なければProductionにインストール - 本物のハードウェアやシミュレータでUIテスト - UIテストは時間がかかるので、Production用のビルドに影響しないように - XCUIAutomation - Obj-C - accessibilityIdentifier: ローカライズに依存しない - accessibilityLabel: 目が見えない人のため - VoiceOver用のステートが入っている - ツール - push simulation - log exposure - Swiftでユーザの操作のようにテストが書ける - enumが使いやすい - nestedなenumにするとコード補完で画面のエレメント構造がわかる - namespaceとして使う - Objc-Cブリッジが不要 - アプリ内のステートを公開する必要はない - 世界中の良質な情報を届ける

SmartNewsアプリの品質への考え方や取り組みについて、2名からの発表でした。

前半はSmartNewsアプリの品質についての取り組みについての発表でした。

SmartNewsはアプリ実装について結構なこだわりを持っているとのことで、なめらかなページめくりや良質な見た目など、UI/UXにとてもこだわっているとのことでした。このあたりは求められる(あるいは目指す)品質が、プロダクトやサービスなどによって全く異なるということをあらためて思い出させてくれました。

後半はビルドサーバやUIテストの実装についての発表でした。

ビルドサーバは2台構成にしていて、新しいものを導入する時には1台のマシンに導入した上で、問題がなければ新しいマシンにも導入する、といったビルド環境を壊さないようにする取り組みは参考になりました。

XCUITestについては、enum名前空間(ネームスペース)として利用する方法は良いなと思いました。静的型付けでIDE上でコード補完が効くSwiftという言語を有効活用した、良い方法だと思いました。

ところでSmartNewsは「世界中の良質な情報を必要な人に送り届ける」というミッション(あるいはビジョン?)を持っているそうです。

”船を作りたければ、海へのあこがれを説け”という有名な言葉がありますが、プロダクトやサービスがこうした向かうべき方向についての軸を持っているのは重要だとあらためて思いました。

短期大規模開発の品質とスピードの両立について in ライブコマース

  • 自己紹介
    • ショッピングのiOS
    • トラベルのiOS/サーバサイド
  • 開発工数3ヶ月で無事故でライブコマースを乗り切った
  • ライブコマース?
    • アプリをタップするとライブ動画が配信される
    • いいね、コメントなどを複数のアプリで共有する
  • ライブ配信
  • やることいっぱい
    • 何からスタートした?
    • デモ関係
      • デモ用アプリを作成
      • デモ要配信サーバを構築
      • デモ用のSocketサーバを構築
    • アプリ側
      • OSSを利用
      • LFLiveKit, socket.io-client.swift
    • ライブ配信サーバ側
      • NGINX
      • nginx-rtmp-module
  • デモアプリのメリット
    • 完成のイメージを固められた
    • 実現への自信が持てた
    • 社内での説得力がます
      • 有識者とのコミュニケーションもできた
    • コードを流用できる
      • 実際に本番コードに組み込んで流用
    • 簡易動作確認環境
      • 問題の切り分けに繋がった
    • 品質・スピードを上げることができた
  • テスト
    • 本番同等環境を使った
    • 50人一斉視聴テスト
      • メールや口頭で人を集めた
    • 実際に出た不具合
      • 端末が熱い
        • Socket接続のエラー時に再接続がされていなかった
        • インスタンスが更新されていなかった
        • フラグを追加すれば解決した
      • いいね(iPhone 5
        • 乱数の生成に問題があった(32bit)
      • ライブ視聴を繰り返すとクラッシュ
        • AVPlayerの破棄がされていなかった
        • 初期化するだけで解決
  • 振り返り
    • Good
      • ミニマム開発から広げることができた
      • 社内から知見のある人を見つけることができた
      • 精度の高いテストができた
    • Bad
      • 省いたところはある

ライブコマースという、アプリからライブ動画を見れて、そこでの「いいね」やコメントなどを複数アプリ感で共有する機能の開発を短期間で行った際の取り組みについての発表でした。

よく言われることですが、実際に動く簡易版の実装を行うことはとても大切、かつ効果的だとあらためて思いました。特に自信への繋がりや、有識者とのコミュニケーションに繋がったのは、とても良いと思いました。

あとは社内で利用者を集めて、本番同等の環境でテストを行うという取り組みはとても良いと思いました。やはり開発時には気づかない不具合も出てくるので、品質を上げるためにはそうした取組は必要だと思いました。

全体を通して、とにかく戦略的に開発を行ったのだという空気を強く感じました。

懇親会

お寿司を頂きました、美味しかったです!

感想

あまり他のiOS勉強会では話題に上がらない「品質」というテーマで、バラエティに富んだ発表を数多く聞くことが出来てとても有意義な勉強会でした。

私があらためて思ったのは、プロダクトやサービスによって品質の考え方は異なるということでした。画一した「品質」というものは存在せず、品質の考え方については結局自分たちで考え抜かなければならない、ということです。

それを実現する上で、様々なプロダクトやサービスにおける実際の取組みが聞ける、というのはとても良いことだと思いました。(おそらく発表者の方々も、他の発表で得るものがあったのではないかと思います)

勉強会の企画・運営をしてくださった方々にお礼申し上げます。

蛇足

久しぶりのブログのせいか、なんだか文章が稚拙な気もしますがご容赦を。

Androidで利用されるGradleバージョンの確認方法

AndroidでGradleのバージョンと言った場合、以下の2種類がある。

Gradle本体

Gradle本体のバージョンは、File > Project Structure > Projectから確認できる。 f:id:yu_dotnet2004:20170804131110p:plain

利用可能なバージョンは以下のURLから確認可能。 https://services.gradle.org/distributions/

gradle/wrapper/gradle-wrapper.propertiesdistributionUrlを変更することで、新しいバージョンの利用が可能。(gradlewがラッパーになっており、自動的に記載されたバージョンがダウンロードされる仕組みになっている)

#Mon Apr 17 18:32:32 JST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-all.zip

例えば上記のように指定して、./gradlew buildすると自動的にダウンロードされてbuildタスクが実行される。

$ ./gradlew build
Downloading https://services.gradle.org/distributions/gradle-3.5.1-all.zip
...
$ ./gradlew --version

------------------------------------------------------------
Gradle 3.5.1
------------------------------------------------------------

Build time:   2017-06-16 14:36:27 UTC
Revision:     d4c3bb4eac74bd0a3c70a0d213709e484193e251

Groovy:       2.4.10
Ant:          Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM:          1.8.0_121 (Oracle Corporation 25.121-b13)
OS:           Mac OS X 10.12.6 x86_64

Android Plugin for Gradle

GradleのPluginとして実装されているもの。

トップレベルのbuild.gradleに利用するバージョンが書かれている。

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.2'

利用可能なバージョンは以下から確認できる。(Gradle本体の必須バージョンも書かれている) https://developer.android.com/studio/releases/gradle-plugin.html

Gradle本体のケースと同様にファイルを書き換えて、Android Studio上からSyncすればOK。

参考URL

http://qiita.com/izuki_y/items/3c20cd1c655fc4fc6127

platform-tools について復習

なんか微妙に理解が怪しくなったので復習。

なに?

adbとかfastbootといったコマンドが含まれるツール群。

インストール

Android StudioSDK Manager からインストール・アップデートできる。 f:id:yu_dotnet2004:20170804122624p:plain

最近は SDK Manager を利用せずとも単独でダウンロードできるらしい。 https://developer.android.com/studio/releases/platform-tools.html

含まれている主なコマンド

adb

android debug bridgeの略。 apkをインストールしたりデバッグしたりまぁいろんなことに使う。

fastboot

  • Fastbootモードという、PCでいうところのBIOS画面のようなものがある
  • 端末がそのFastbootモードで起動していれば、fastbootコマンドで操作することが出来る
  • adb reboot bootloaderコマンドで、再起動後にfastbootモードで立ち上がる
  • フラッシュメモリへの書き込みや、フラッシュメモリ上のパーティションの管理などが行える
  • カスタムリカバリの書き込みを行うことも出来る

参考URL

https://jagadgetaholic.blogspot.jp/2015/12/fastboot-command.html http://news.mynavi.jp/column/androidnow/043/

iOSDC JAPAN 2017 に CfP を出してみた

iOSDC JAPAN 2017 に CfP を 3本出しました。 iosdc.jp

”当たらなければどうということもない”なんて名セリフもありますが、たしかに当たらなければ発表できません。 (悲しいけどこれ抽選なのよね)

なのでせっかくなのでシェアしたいと思います。 もし「これ聞きたいかも」と思うものがあれば、リンク先のページでブックマークやらTweetしていただけるとです。

実践テスト駆動開発 in iOS 15分 or 30分

iosdc.jp

TDD(テスト駆動開発)をご存知の方も多いかと思いますが、iOSではあまりテスト駆動開発の適用例を聞かない気がしています。 おそらくその理由の一つとして「UIが頻繁に変わるから向いてない」ということなのでしょうが、果たして本当でしょうか?

iOSにおけるUIテスティングは確実に進化しています。

本セッションではライブコーディングを取り入れながら、 実際にどのようにテスト駆動開発を適用していけるかをステップ by ステップで紹介したいと思っています。

UIテストの実行時間の短縮に挑戦する(LT)

iosdc.jp

UIテストを作成することで、リグレッションテスト(回帰テスト)の自動化といった恩恵を受けられるようになります。

しかし、往々にしてUIテストは実行時間がネックになります。 個人的な意見ですが、”実行に時間がかかる”というのは見た目以上のコストがかかっており、実際に掛かった時間以上のものを奪うと思っています。 (モチベーション、集中力低下によるパフォーマンスの低下、etc)

本セッションでは、UIテストの実行時間をどこまで短縮できるか挑戦した結果を発表したいと思います。 それによって、実際の開発にUIテストを導入する障壁を少しでも低くすることができればと思っています。

Swiftzで学ぶ関数型プログラミング

iosdc.jp

ここ数年で関数型プログラミングはかなり注目され始めています。

Swiftを作る際には関数型言語Haskellが参考にした言語の一つとして紹介されていますし、 実際、Swiftは関数型プログラミングパラダイムとしてサポートしています。

しかしながら、関数型プログラミングなかなかとっつきづらく難しいテーマであるのも事実です。

本セッションではSwiftでの関数型プログラミングをサポートするライブラリである「Swiftz」を用いつつ、 関数型プログラミングとはどういうものかという入門的なお話をできればと思っています。

最後に

まぁそんな感じです。よろしくお願いします。

ブログデザインを変更

ひと目で気に入ったので、導入させていただきました。
hitsuzi.hatenablog.com

Otemachi.swift #01 に参加してきた

参加してきました。 nikkei.connpass.com

今回はブログ枠ではないですが、簡単にダンプした内容をメモです。

会場

発表に集中しやすい落ち着いた雰囲気の会場でした。

すべての椅子にテーブルがついていて参加者にもやさしい感じだと思いました。 (ただし僕が座った椅子のテーブルは3度くらい傾斜があったような気がします・・・)

ちなみに懇親会は隣にあったタリーズで行ったのですが、タリーズとは思えないオシャレ感(失礼)で驚きました。 (ちなみに僕はカフェの中でもタリーズの利用率が一番高いです)

発表

WWDCの感想

  • 難易度
    • ハード
    • 抽選に当たる必要がある
    • チケット代高い(20万)
    • 周辺のホテルがめちゃくちゃ高い
    • 一泊$300
    • 早めに確保しよう
    • 10時間
  • 工事中だからサンフランシスコじゃなかった(らしい)
  • 空港についてから1.5h
  • 受け入れムード?
    • Uberの運転手でもWWDC知らない人はいた
  • 前日チェックイン
    • 入館証と上着をもらう
  • Keynote
    • 入るまでも並ぶ(2, 3時間)
    • CEO、役員クラスが話す
    • 開発者よりビジネス向け
    • 新しいiOSバージョン
    • ハードウェア
  • OPムービー
    • The world is depending on you.
  • Keynote
    • Mac/iOS11/iPad Proあたりが盛り上がった
    • HomePodもわりと盛り上がっていた
  • あと
    • 会場に残って、偉い人と写真撮影
    • 昼食を早めにゲット
  • Platforms State of the Union
    • 開発者向けの内容
    • Keynote よりは断然座れる
    • What’s new をまとめた内容
  • ハンズオン
    • 芝生エリアもあった
  • ラボ
    • Appleのエンジニア、デザイナなどに質問したりレビューしてもらう
    • 達人はBashで再開するらしい
    • 当日の7時くらいに予約しないと
  • ふりかえり
    • 乾燥していて意外と涼しい

開発中のアプリをXcode9 & Swift4に移行しました

speakerdeck.com

  • ハマったところ
    • ライブラリがコンパルエラー
  • Xcode9
    • コンパイル速度
      • Swift 1万step
      • クリーンビルドでは全く変わっていなかった
      • 差分コンパイルが気持ち速くなった?
      • エディタは速くなっていたので快適になった
    • Wireless Development
      • クリーンだと2〜3倍遅い
      • 差分ビルドだとWifiの方が速い場合もある
    • まとめ
      • Xcode9は素晴らしい
      • 開発者にも目を向けて改善してくれた

Swift Package Manager

speakerdeck.com

  • WWDC
    • San Jose には行った
    • いろんな人に会えたので良かった
  • Version 4 での変更内容
    • 殆ど語られていない
  • What’s New in Swift
    • 02:25/slide7
    • 7000+
    • サーバサイドで使われることが多い
  • 大きな変更があった(と語られたが)
    • そんなになさそう?
    • Swiftの言語バージョンを指定するのは前から
  • Swift Evolution を調べた
    • SE-0158
    • SE-0162
    • SE-0149
    • SE-0150
      • ワークフローの改善?
      • Gitブランチも指定できるように
    • SE-146
      • プロダクトを定義できるようになった
      • 他のパッケージに対してどのターゲットを公開するか?
    • SE-175
      • .resolvedのファイルが出来た?
  • マニフェストファイルの変更
    • exclude がなくなった?
  • まだ試してない
  • まとめ
    • コマンドラインツール/サーバサイドあで活用
    • Version4への移行がスムーズっぽい?
    • Linux上でほぼおなじライブラリが使えるから使う
      • ほぼの話
    • まだまだ発展途上?

Codableことはじめ

speakerdeck.com

  • 自己紹介
    • takasek
  • Codableとは
    • Swift4の機能
    • JSON/plistをエンコード・デコード
    • いろいろなところで発表されてる
      • 3日連続のピックアップ
  • イケメンポイント
    • Codableを宣言するだけでOK
    • カスタマイズも出来る
    • どんなEncoder/Decoderでも利用できる
  • CSVデコード出来る
  • カスタマイズ
    • コンパイラが型のメンバを自動生成している
    • 既存実装を上書きできる
    • キー名のカスタマイズ(codingKey)
    • デコードロジック(init)
  • とにかく自動生成してくれる
    • クラスの継承も考慮してくれる
    • Encoding Strategiesを決められる
  • 組み込みのエラー制御
    • 詳細なエラー情報を投げてくる
    • 予期しないエラーはこない
    • 独自のチェックも入れられる
  • エンコーディングの詳細をカプセル化
  • 抽象化されたフォーマット

XCTest.framework

  • 自己紹介
    • 学生
    • アルバイト
    • テストを書いていないって人が多いから
  • Xcode 9.0
    • 非同期テスト
      • 実装がXCTestCaseから独立
      • 非道記事のエラーが具体的に取れるようになった
      • タイムアウトでも成功になるような処理が書ける
      • 何をwaitするのか明確に
      • waitForExpectations
        • XCTestErrorが返ってくる
      • func wait
      • XCTWaiterのfunc wait
        • delegateを書ける様になったり
      • 扱いやすくなった
    • teardown
      • 個々のメソッドにteadownを入れられるようになった
      • 積むタイミングには注意
    • 名前ありアクティビティ
      • サブステップに分割出来るようになった
      • runActivity
      • ログが分かりやすくなる
    • マルチApp
      • 複数のアプリを扱えるようになった
      • launch()に加えて、activate()が増えた
      • 列挙型で状態が取れる
    • UI Elements
      • wait処理が増えた(existsがtrueになるまで待つ)
      • firstMatchで早く処理できる
    • screenshot
    • 保存
      • XCTAttachment
      • テスト中の情報を保存できるようになった
      • 成功時に残しておく場合は.keepAlwaysする必要あり
      • まとめて保存する方法ない?

High Efficiency Image File Format (HEIF)について話します

speakerdeck.com

  • 自己紹介
  • 新しい画像フォーマットに対応
    • HEIF
    • 今日は概要とデモだけ
  • JPEGデファクト
    • 連続した画像をうまく保存できない
    • 深度情報を保存できない
  • Appleが求めた画像フォーマット
    • HEVC
      • コーデック形式
      • H.264に比べて2倍の圧縮性能
      • 今後10年はサポート
    • HEIF
      • 連続する画像の保存圧縮
      • アニメーション
      • 国際規格
  • HEIFファイル
  • HEIF作成にはNokiaのライブラリを使う?
  • 実験
    • 朝刊の紙面情報を試してみた
      • 3000 x 4044 px
      • JPEG > HEIF > WebP
      • JPEGと比較しても拡大ノイズが少ない
    • 電子版アプリ
      • JPEGとくらべて20%通信量削減
  • 最後に
    • WebPと違ってハードウェアアクセラレーションが効く
    • 通信量をJPEGより削減できる
    • 素数が多い画像だと効果が高い?
    • ドキュメントが少ない

More Swift App Startup Time

speakerdeck.com

  • アプリの起動速度を高速化
  • 困っていること
    • dLibを増やすとアプリの起動速度が遅くなる
    • SwiftのライブラリはDynamicFrameworkとしてリンクされる
      • Carthage
      • CocoaPods
    • 増やすと遅くなる
  • おすすめセッション
    • App Startup TIme
    • Optimizing App Startup Time
      • 計測と改善
  • dyld3(現状2.x)
    • 新しいDynamic Linkerを作ってる
    • 実用段階になったらリンク早くなる
  • 計測手法と改善方法
    • Optimizing Startup Time Lab
      • めちゃくちゃニッチなLabがあった
  • Labで聞いたこと
    • dLib増やしすぎると遅くなる?
      • YES
    • namespaceないし、frameworkないとつらい
      • Prefixつければ?
    • iOS11で入るか?
      • まだ言えない
  • 起動時間の計測方法
      1. Instruments を使う
      2. Time Profiler
      3. Static Initializer Call
      4. 見られる
      1. Instruments via CLI
      2. 出力データはGUIじゃないと閲覧できない
      3. バイナリなのでパースできない
      1. task_info + UI Test
      2. didFinishLaunchで起動時間をとる
      3. CPU時間をとる(スマートではないけれど)
      1. DYLD_PRINT_
      2. dylibの読み込み時間が出力されるようになる
  • まとめ
    • 遅いのはどうしようもない
  • Labで生き残る方法
    • 台本を作っていく
    • ゆっくり喋ってくれ
    • 同じことを2人以上に聞く

Core ML

  • 自己紹介
  • Vision Framework/NLP
    • 学習済みモデルをAppleように変換?
  • CoreML in depth をみれば分かる
  • Q
    • なぜこれを作った?
    • マルチスレッドで使える?
      • 動かせた
    • モデルを分けて同時に利用できる
    • モデルファイルは500MBくらいある
  • まとめ
    • 使うのは簡単
    • モリーはめちゃくちゃ食うよ

5分でわかる Password AutoFill for Apps

speakerdeck.com

  • 自己紹介
  • Password AutoFill for Apps
    • Safariのパスワード自動入力と同じものをアプリで
    • Safari上のWebでログイン済みで無いとダメ
  • なぜこの機能に注目した?
  • 実装方法
    • アプリ
      • ContentTypeにusername/passwordが追加された
      • Associated Domains にWebサービスを設定
      • アプリ側にも設定
    • サーバサイド
      • ファイルを置く
  • 実装したけど鍵マークが表示されない
    • シミュレータでも実機でも出ない
  • まとめ

CoreNFC

speakerdeck.com

  • NFCAPIが来た
    • セッションはなかった
  • メリット
    • 直感的
    • 互換性
    • 安い
  • Reader/Writerモード(実装必須)
  • 八重洲NFC専門ショップ
  • ユーザをトラッキングしたり(イベント)
  • Wiiのコントローラーにも
  • iPhone7とiPhone7+

感想

全体的に濃い内容が多く、非常にためになる内容ばかりでした。 個人的には新しいXCTestの内容に非常に興味をもちました。