やらなイカ?

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

LINEの新しいMessaging APIを試してみた

先日開催されたLINE DEVELOPER DAY 2016で発表された*1BOT APIに替わるMessaging APIを試してみました。

business.line.me

まず、先日作成した「調整さんリマインダBOT」をMessaging APIに移行。ついでに、新機能であるTemplate Messageと、グループやトークルームにBOTを参加させたときの振る舞いを確認します。

nowsprinting.hatenablog.com

Messaging APIへの移行

BOT用アカウントの作成

BOT API Trialのアカウントは利用できないので、新規にビジネスアカウントを開設します。手順は以下の通り。

  1. LINE Business Center -> アカウントリスト -> ビジネスアカウントを作成する
  2. 作成したアカウントの LINE@ MANAGER -> アカウント設定 -> Bot設定
  3. APIを利用する”を選択(この時点でアカウントは、LINE@アプリ、1:1トーク、お店トークが利用できなくなります)
  4. 必要に応じてBotの設定を行なう
    • Webhook送信(デフォルトは"利用しない”)
    • グループトーク参加(デフォルトは"利用しない”)
    • 自動応答メッセージ(管理画面で設定。デフォルトは”利用する”)
    • 友だち追加時あいさつ(同上)

自動応答メッセージおよび友だち追加時あいさつは"利用する"であっても、ちゃんとWebhookが飛びます。メッセージの内容は「メッセージ」から変更できます。

その他、LINE@ MANAGERでBOTのアカウントページなども設定できます。

App Engineアプリケーションの作成

App Engineアプリケーションは(version指定するなどして)使いまわしても良かったのですが、今回は新規に作成しました。

先の記事と同様にWebhook URLを設定し、Channel SecretChannel Access Token*2の取得を行ないます。Channel IDおよびChannel MIDは不要になりました。

BOT SDKの更新

Golang用のSDKもアップデートされているので、下記コマンドで更新しておきます。

$ goapp get -u github.com/line/line-bot-sdk-go/linebot

BOT本体の変更

主な変更点は以下の通り。

  • LINE BOTの初期化において、Channel IDおよびMIDが不要になったので削除
  • 代わりにACCESS_TOKENが必要になったので追加
  • linebot.OpTypeがなくなったので、stringに変更
  • OpTypeの判定は全てlinebot.EventTypeに変更。ここの判定はかなりスッキリした
  • APIの変更に対応
    • 大きな変更点として、ユーザの発言に応答するReply Messageと、BOTが起点となって発信するPush Messageに二分されました。今回のBOTは仕様上Pushを利用しましたが、通常のBOTではReplyを使っていくことになるでしょう。
  • テストの修正
    • X-Line-Signatureヘッダキーの変更(もとはX-LINE-ChannelSignature)
    • 友だち追加テストデータ(json)のフォーマット変更

以上でテストを通し、goapp deployして動作確認を行ないました。詳細・差分はGitHubに置いてあるソースを見てください。

github.com

なお、旧BOT APIで取得できていたユーザの識別子であるMIDは、Messaging APIで取得できるUserIDとは異なります。 先頭文字はユーザはU、グループはCトークルームはR。+英数32文字(16進数?)という書式になっていました。

Template Message

新機能のTemplate Messageは、トーク上に複数のボタンなどで構成されるテンプレートを表示し、ユーザの応答を得るための仕組みです。Buttons, Confirm, Carouselの3種類を利用できます。

現時点でいくつか制約があるようです。気づいたものが以下。

  • iOS版のLINEアプリ(バージョン 6.6.2)では、Template Messageは「LINEアプリのバージョンが古いため(ry」と表示され、利用できません
    • バージョン 6.7.0で対応されました
  • Android版のLINEアプリ(バージョン 6.7.0)では利用できました
    • バージョン 6.5.1では「ご利用のバージョンでは対応していないか不正なURLです」と表示され、利用できませんでした
  • ButtonsおよびCarouselにおいて、APIリファレンスにはthumbnailImageUrlが省略可能と書かれていますが、省略するとエラー*3が返ります*4
  • [11/2追記]Template Messageに設定するaltTextは「非対応端末で表示される代替テキスト」と説明されていますが、Notificationおよびトーク一覧画面での表示にも使われています(クライアントバージョン 6.8.0時点)

グループおよびトークルーム

従来のBOTはユーザと1:1のトークしかできませんでしたが、Messaging APIでは、グループおよびトークルームへの招待が可能となりました。

こちらも現時点でいくつか制約があるようです。気づいたものが以下。

  • グループに複数のBOTを招待できない。2つ目以降のBOTは「招待中」のまま保留され、Webhookのjoinも飛ばない
  • トークルームは、招待したタイミングではjoinは飛ばない。招待後、最初のメッセージがWebhookで送られる直前にjoinが投げられる
  • トークルームにも複数のBOTを招待できない。2つ目のBOTを招待すると、即退出される(元々いたBOTが退出するケース、2つ目が退出するケースの両方を観測)
  • トークルームのメンバーはキックできない仕様なので、BOTは自らLeaveAPIで退出しない限りルームに一人取り残される。ルームの参加人数を知ることもできないので、取り残されたら抜ける手段はなさそう
  • グループおよびルームからのWebhookでは、そのメッセージがどのユーザの発言かを知ることはできない(送信元はGroupIdおよびRoomIdとなる)。Template MessageからのPostback Eventも同様。
  • ユーザと1:1のトークでは、ユーザの表示名などはGet ProfileAPIで取得できるが、グループおよびトークルームのメッセージ送信元(GroupIdおよびRoomId)の情報を得ることはできない
  • BOTの送信したメッセージは、BOTには送信されることはない(オウム返しするBOTで、無限ループを気にする必要はない)
  • [10/30追記]すでにBOTがグループに所属している状態で他のユーザを招待すると、再びWebhookでjoinイベントが送信される。タイミングは「参加」時ではなく「招待」時点。意図がわからない。

参考

*1:参加したわけでなく、LINE LIVEで見てました。Beacon欲しかった…

*2:Tokenは、横の[ISSUE]ボタンをクリックすると発行されます

*3:linebot: APIError 400 A message (messages[0]) in the request body is invalid

*4:SDKでなくLINEのバックエンドの仕様のようですが、どこに申告すればいいのでしょうか?

調整さんリマインダLINE BOTを作ってみた

調整さん」を(日程調整ではなく)出欠の管理に使っている前提で、出欠登録のリマインダをLINE BOTとして作ってみたので、そのメモ。

[9/29追記]LINE BOT APIはDeprecatedとなり、Messaging APIに置き換わりました。同時にSDKもMessaging API対応のものに更新されていますので、ご注意ください。

[10/23追記]LINE BOT API Trial Accountは、11/16に完全削除を予定されていると発表されました。

新しいMessaging APIへの置き換えについては、次のエントリを参照してください。

nowsprinting.hatenablog.com

環境

仕様

  • 調整さんのイベントをクロールし、3日後に開催される予定がある場合、参加人数などを通知する
  • 通知は、BOTと友だち登録した人全員に送られる(cronで毎朝8:00に起動)
    • LINEグループに参加させたかったが、BOTはLINEグループに参加させることはできない*1
    • BOTの友だち登録は、デフォルトでは50人まで
    • BOTはid検索できないため、友だち登録は次の手順で行なう:QRコード画像を送る→画像を端末に保存→LINEで[友だち追加]→[QRコード]→[ライブラリ]→保存したQRコード画像を選択
  • 監視対象の調整さんイベントは、ハッシュをapp.yamlに記述しておく
  • LINE BOTのシークレットキーなどもapp.yamlに記述

GAE/Goのプロジェクト作成

Google Cloud Platformコンソールで新規プロジェクトを作成。プロジェクトIDを確定させる。

LINE BOT(チャンネル)の開設・設定

  • BOT API Trial Accountを開設
  • 開設したチャンネルの"Basic Information"にある"Callback URL"に、AppEngine側のコールバックを受け取るURLを設定
    • "https:// YOUR_PROJECT_ID .appspot.com:443/line/callback"
  • "Server IP Whitelist"は設定しない。設定するとAppEngineのIPアドレス変動に対応する必要があるため

必要なパッケージのインストール

$ goapp get github.com/line/line-bot-sdk-go/linebot
$ goapp get golang.org/x/text/encoding/japanese
$ goapp get golang.org/x/text/transform

LINE BOTの基本部分を実装

LINEからのコールバック用のハンドラをfunc init()に定義する。パスは上で定義したもの。

func init() {
    http.HandleFunc("/line/callback", lineCallback)
}

lineCallback()の中身は、ほぼBOT SDKのサンプルをコピペ。ただし、App Engineではhttp.Clientとしてappengine/urlfetchを使用する必要があるため、LINE BOT Clientからもこれを使うように、下記のように初期化する。

bot, err := linebot.NewClient(channelID, channelSecret, channelMID,
                        linebot.WithHTTPClient(urlfetch.Client(c)))

ここで一度goapp deployして動作確認。BOTを友達登録して、トークで送った内容をオウム返ししてくれればok.

友だち登録時にMIDを取得してデータストアに格納する

LINEへのメッセージ送信には、toにMID(LINEユーザの固有ID)を指定する必要がある。友だちに一斉配信するためには、あらかじめ友だち登録時に相手のMIDを取得しデータストアに保存する。

友だち登録および解除は、メッセージとは別フォーマットのオペレーション・イベントが通知される。パラメタのOpTypeによって、追加(ブロック解除も等価)、削除を判別できる。

なお、メッセージとは送信者のMIDが格納されている位置が異なる。OperationContent.Params[0]に入っているので注意。

    if content.IsOperation {
        //オペレーションイベント受信
        opContent, err := content.OperationContent()
        if content.OpType == linebot.OpTypeAddedAsFriend {
            task := taskqueue.NewPOSTTask("/task/addfriend", url.Values{
                "mid": {opContent.Params[0]},
            })
            taskqueue.Add(c, task, "default")
        } else if content.OpType == linebot.OpTypeBlocked {
            task := taskqueue.NewPOSTTask("/task/removefriend", url.Values{
                "mid": {opContent.Params[0]},
            })
            taskqueue.Add(c, task, "default")
        }
    } else if content.IsMessage && content.ContentType == linebot.ContentTypeText {
        //テキストメッセージ受信
        (snip)
    }

Task Queue/task/addfriendは送信者のMIDをデータストアに保存、/task/removefriendは送信者のMIDをデータストアから削除するもの。

メッセージを全員に送信

テキストメッセージを受信したとき処理を、送信者にオウム返しするもの(サンプルの実装)から、データストアに保存されている全MIDへの送信に変更する。

//データストアから購読者のMIDを取得
q := datastore.NewQuery("Subscriber")
var subscribers []Subscriber
if _, err := q.GetAll(c, &subscribers); err != nil {
    return err
}
mids := make([]string, len(subscribers))
for i, current := range subscribers {
    mids[i] = current.MID
}

//全員に送信
bot.SendText(mids, message)

調整さんリマインダ処理

調整さんリマインダは、下記処理をcronで毎朝起動されるようにする。

調整さんのイベントをクロール

cronからキックされるハンドラをfunc init()に追加する。

http.HandleFunc("/cron/crawlchouseisan", crawlChouseisan)

以下、crawlChouseisan()の実装。

調整さんのAPIは公開されていないが、イベント作成者にのみ表示される「出欠表をダウンロード」リンクのURLから、csv形式のレスポンスが得られるのでこれを利用する。

調整さんのイベントハッシュは、イベント作成時にコピーしておき、app.yamlに定義したものを取得する。

url := "https://chouseisan.com/schedule/List/createCsv?h=" + os.Getenv("CHOUSEISAN_EVENT_HASH")
c := appengine.NewContext(r)
client := urlfetch.Client(c)
res, err := client.Get(url)

csvをパースする。MS932なのでjapanese.ShiftJISデコーダを使用している。

また、encoding/csvReaderは、1レコード目と異なるカラム数のレコードをエラーと判断する。調整さんのcsvは1レコード目がタイトルのみとなっており、素直に読むとデータ行で"wrong number of fields in line"というエラーメッセージが出てしまうので、下記にようにErrFieldCountは無視するようにした。

reader := csv.NewReader(transform.NewReader(csvBody, japanese.ShiftJIS.NewDecoder()))
for {
    row, err := reader.Read()
    if err == io.EOF {
        break
    } else if e2, ok := err.(*csv.ParseError); ok && e2.Err == csv.ErrFieldCount {
        //フィールド数エラーは無視
    } else if err != nil {
        log.Errorf(c, "Read chouseisan's csv failed. err: %v", err)
        return nil
    }
    (snip)

日付などのパース処理は割愛。日付をキーにしたMapに詰める。

3日後の予定があれば通知

AddDate()で3日後を指すDateを作り、Mapにその日のValueが存在すれば通知対象とする。なお、App Engine実行環境ではUTCが使われるため、JSTを明示的に使用する。

tz, _ := time.LoadLocation("Asia/Tokyo")
today := time.Now().In(tz)
(snip)

//3日後の予定をピック
after3days := time.Date(today.Year(), today.Month(), today.Day(), 0, 0, 0, 0, tz).AddDate(0, 0, 3)
obj, exist := m[after3days.String()]
obj, exist := m[after3days]
if !exist {
    log.Infof(c, "Not found schedule at 3 days after.")
    return
}

//メッセージを組み立てて送信
(snip)

cron.yamlを作成

作成したハンドラを、毎朝8:00 JSTに起動されるようにcronを定義。

cron:
- description: crawl chouseisan every day
  url: /cron/crawlchouseisan
  schedule: every day 08:00
  timezone: Asia/Tokyo

以上をデプロイして、今のところ想定通り動作している模様。

途中、シネマカリテで『不思議惑星キン・ザ・ザ』<デジタル・リマスター版>を観た影響で、ひたすら「クー」と返す "キン・ザ・ザBOT" に作り変えたくなったけど、よく我慢した。

ソース

下記リポジトリで公開しています(Apache License 2.0)

GitHub - nowsprinting/ChouseisanReminder: Reminder line-bot for chouseisan.com

はじめてのGoでありAppEngine/Goアプリなので、作法とかなっとらん自覚はあります。なので参考程度に。『みんなのGo言語』ポチったので届いたら読んで改善したい。

参考

*1:りんなはグループに登録できるので、トライアルの仕様?

Unite 2016 Tokyo に行ってきました #unite2016tokyo

Unite 2016 Tokyoに行ってきました。去年はVR系セッション優先で聴講しましたが、今年はVR、最適化、絵づくりあたりを満遍なく。

講演資料は下記ページで即日公開、動画も近々公開されるということで、印象深かったメモのみ。

Unity - Unite 2016 Tokyo 講演ガイド

DAY 0

トレーニングデイのコース A「Unity サービス実装ワークショップ」に参加してきました。Unityのクラウドサービスである Cloud Build, Analytics, In-App Purchase, Adsなどの体験ワークショップ。

Cloud Build以外は使っていなかったのですが、AnalyticsとGame Performance*1は適用も簡単で、早速自社プロダクトに組み込んでFabric*2と比べてみようかと。

このワークショップでは、教材のプロジェクトだけでなく、UnityのインストーラWin32/64/MacOSX)まで入ったUSBを配布され、プロジェクトはごく最小限のエディタ操作とコピペで動作、さらにはCloud Buildの体験にはGitHub上のプロジェクトをforkするだけと、かなり至れり尽くせりの(安全サイドに倒した)ワークショップでした。

この手のワークショップ、これまで主催側として上手く行った例がないのですが、やはりこれくらい準備しないとダメですね。見習いたい。

DAY 1

モバイル端末向けのUnityアプリケーションの最適化実践テクニック

  • 最適化の前に、良いデータを計測することが大事。各種プロファイリングツールの紹介、使いかた
  • テクスチャ、モデル、オーディオの最適化
  • メモリのフラグメンテーションに注意する*3
    • boxing, foreach*4, 文字列操作に注意
    • JSON操作は、Unity 5.3以降ならJsonUtilityを使うこと
  • PrefabやCanvasを適切に分割する

実践!Oculus Rift - VR開発テクニック

  • Oculus RiftおよびGear VR向けアプリをストアに掲載する申請ができる
  • バイナリは、Store/RC/Beta/Alphaの4種類(4フェーズ)アップロードできる
  • カテゴリにある「先行アクセス」はBeta版などに使える。「コンセプト」はフルゲームでないアプリでも申請可能。
  • 必要なストア画像などはOculus Store Art Guidelineを参照
  • 価格もOculusで審査される。法外な値段はリジェクトされる
  • VR酔いの程度を三段階にレーティングされる

ハードウェア性能を引き出して60fpsを実現するプログラミング・テクニック

DAY 2

ホンモノ志向のVR空間づくり

  • 3Dをどう見ているか
  • リアル世界で生活して経験を積んでいるので、写真でも立体や遠近感を得られている
    • 知っているものがあるとスケールがわかり、距離もわかる。道路標識とか
    • 線で描かれた立方体を、立方体だとわかる
    • この感覚は、13歳くらいで習得できるらしい
  • 静止画ではわかりにくくても、動いているとわかる
  • Oculus Frameworkの"Teleportation"は参考になる
  • 黄金比、フィボナッチ、Rule of 3rd。科学的根拠はない
  • Light/Shadow
  • VRコンテンツを作るとき、単眼でテストする。それで十分立体感を得られるならば、二眼にすればよりよく見えるはず

映像制作のゲームチェンジャー:メイキング オブ ”THE GIFT”

  • カラーボールのシーンは、頂点数がIntegerの最大値を超えてマイナス表記になった
  • ボールは複数あわせて1メッシュにしている
  • ボールのポリゴンは実は粗い。少し小さめの円形に切り取るシェーダで球体に見せている
  • デザイナさんがエディタで作業できるレベルで動く

marza-realtime.com

Fate/Grand Orderにおける、ディライトワークス流Unity活用術

  • バトルキャラはビルボード。槍など横に振り回すもの(奥行き表現があるもの)は3D。Mayaで作ってfbxエクスポート。
  • エフェクトなどMayaでは表現が難しい物はUnity側でエフェクトをPrefabにしてぶら下げる
  • モーションはPlayMaker。カメラ移動、エフェクトの再生もここで。キャラ・モーションが増えたときもActionの追加で済ませる(アプリ更新を避けられる)
  • 宝具(必殺技)演出はuSequencer(カットシーンエディタ)で作成
  • ゲーム開発環境とは別環境の「宝具制作環境」。実行ボタンを押すと宝具が再生される。デザイナが素早く開発できる
  • Unityエンジニア=プランナーでもデザイナーでもプログラマでもない。今後増えてくる役割かも?

マルチシーン編集の使い方

  • Unity 5.3から
  • シーンを並べるほか、レイヤのような使いかたで編集の競合を避けられる
  • ヒエラルキにシーンをD&Dすれば追加で開く
  • 新しいSceneManeger API

Unityとアセットツールで学ぶ「絵づくり」の基礎(ライト、シェーダー、イメージエフェクト)

  • Unity 4までの絵づくり、Unity 5からの絵づくり
  • ぼくのUnityと違う
  • 「色を塗る」のではなく、「光を反射させて色を出す」。要素は、マテリアル×ライト×カメラ
  • 間接光

Making of The Modern Zombie Taxi Co.

  • オブジェクトの選択は、Look at Button (Gazing) が最適
  • 入口をリビングルームにした。プレイヤーをVRに慣れさせる空間
  • ダッシュボードに表示する情報は、通常のゲームより少なくしないとノイズにしかならない。当初11項目を2項目に減らした
  • 道案内も工夫。VR空間内を見てもらえるように、オブジェクトとして配置。
  • チュートリアルも難しい
    • VR経験のない人は、VR世界に興奮する。ゲームを遊ぶことを忘れる。
    • シンプルに、最低限に、同じ情報を繰り返し提示する。
  • ほか、VRでやってはダメなことも試してみた結果なども紹介されていて面白い

所感

トレーニングデイも、懇親会も、そして講演も、楽しめました。 今年から導入された有料S席システムも利用しましたが、聞きたい講演を確実に、しかも並ばず入れるのは快適。

最近よく言われる、講演中のシャッター音問題も、各回開始前アナウンスの徹底の効果なのか、スライド全ページ撮影するような人は見当たらず。ほかのカンファレンスもこうなるといいですね。

関係者、登壇者の方々、ありがとうございました!

*1:クラッシュログを採取してくれるサービス。BetaなのでProライセンスが必要

*2:先月Unity SDKがリリースされた。AnswersとCrashlyticsがそれぞれAnalyticsとGame Performanceに競合

*3:この話題は携帯電話Javaやってた頃を思い出す

*4:arrayをヒープにコピーしてしまう

#tryswiftconf 3日目のテスト系セッションまとめ

3/3〜5の3日間、サイバーエージェントさんのセミナールームで開催されたtry! Swift、その3日目に行ってきました。 海外からも100人を超える方々が参加され、とても活気のあるカンファレンスでした。

Swift言語にフォーカスした本カンファレンスですが、3日目は開発者テストに関するセッションが2つありました。圧倒的にスイフト力が足りない私なので、これらのセッションについてだけ書きます。

Swiftにおける実践的なモック化について

Veronicaさんのセッション。Objective-C動的言語なので、テストコードでモック(を含む、テストダブル)を使ってプロダクトコードやフレームワークの挙動をランタイムに置き換えることができていました。またそれを容易に実現するためのOCMockというライブラリもありました。

しかしSwiftではこれができないため(反面、安全と言えます)、どうモックを使っていくべきか?というセッションです。

そもそも、モックを使う理由

続いて、DI(Dependency Injection: 依存性の注入)を使う理由

  • カスタマイズ(置き換え)が容易にできる
  • オーナーシップが明確になる
  • テスタビリティ(試験性、テスト容易性)を上げる

テストダブルの代表的なもの

  • スタブ(メソッドの戻り値を置き換える)
  • モック(メソッドの呼び出し、引数を検証する)
  • パーシャル・モック(特定のメソッドだけ置き換え。これは『xUnit Test Patterns』の定義・分類ではなく、恐らくOCMock独自の言葉)

パーシャル・モックのアンチパターン

  • setUp()でモックを定義するのが大変になる
  • What's real? What's fake?

モックを自分で作る場合、本物のサブクラスを作るのでなく、そもそも必要なものをProtocolとして定義しておく。Javaでも原則Interfaceを書くという宗派がありましたが、それ*1

「ロールをモックせよ」の話。むやみにモック化しない。壊れやすいテストになる。

Swiftで将来的に使えるようになりそうな?モックフレームワークの紹介(リンクはぐぐったもの)

GitHub - rheinfabrik/Dobby: Swift helpers for mocking and stubbing

GitHub - DeliciousRaspberryPi/MockFive: A Mocking Framework for Swift Unit Tests

GitHub - mflint/SwiftMock: A mocking framework for Swift

GitHub - SwiftKit/Cuckoo: First boilerplate-free mocking framework for Swift!

CuckooはMockitoインスパイア系っぽい。

QA

  • 日付や時間に依存するテストはどう書くべき? -> 遭遇したことがないけど、考えてみるのも面白そう

@niwatakoさんの書き起こしも参照。

niwatako.hatenablog.jp

所感

Objective-Cでは(OCMock/OCMockitoでは)依存オブジェクトの扱いが雑な設計であっても、ランタイムにモック化することで強引にテスト可能だったりしたのですが、Swiftではちゃんと設計しないとダメだよね、という話。

またSwiftに限らず、モックは便利さにつられて乱用しがち、そしてフラジャイルな(壊れやすい)テストになりがち、という点まで、短い時間で正しく警告されていてすばらしいセッションでした。

モックに頼らない、という点は、設計の見直しで回避できるものももちろんありますが限界もあるので、より上層の(結合度の高い)テストレベルで担保するなどが最適解かな、と考えています*2。このセッションではそこまで詰め込めなかっただけだと思う。

なお、Swiftの一部メソッドを(Objective-Cの機能を使って)動的に置き換える手段について、Danielさんの"Code Injection from scratch"セッションで触れられていました。こちらも@niwatakoさんの書き起こし参照。

niwatako.hatenablog.jp

An Artsy Testing Tour

Ashさんのセッション。Artsyではこれまで4つのアプリに対してテストを書いてきたが、それぞれアプローチが違うので紹介する。

前提として、

ひとつ目のアプリ

  • テストは無かった。Apple TVローンチまで時間がなく、担当者一人で作ったもの。
  • テストを書くべきか否か、バランスを判断。コードベースが小さいものだったので、ローンチを優先した。

ふたつ目のアプリ

  • はじめテストは無かったが、コードベースが大きかったのでリグレッションテスト強化のため、後からテストを追加した。
  • "Bus factor"*3防止のため、テストのドキュメントとしての役割を重視*4
  • DIを多く使った
  • RSpec*5を使い、セットアップの共通しているテストはbeforeEachにまとめるなど、テストコードをリファクタリング。リーダブルなテストに。
  • 余りにネストしたcontext*6はおかしいので、設計を見直す。十分に説明的な名前がつけられるか?
  • テストはドキュメント
  • 振る舞いをテストする。コードをテストするのではない。

みっつ目のアプリ

  • これもはじめテスト無し、後で追加した。
  • ネットワークアクセスのあるアプリで、開発者も多く実装も散らばっていた。
  • はじめiPhone用、後にUniversal Appに。コンテキストが異なるので、一揃いのテストスイートを、iPhoneiPadふたつの観点からテストを実施した。
  • 大きなクラスからテストしたが、修正が入ると既存テストの書き換えも多発した。
  • Snapshot Test。画面のスナップショットを撮ってpngで保存し、ピクセルベースでdiffを取り合否判定*7。pull-requestの中にスクリーンショットがあるのでレビューしやすい。
  • 小さいクラスからテストすべき
  • 追加したコードにはテストを書く

よっつ目のアプリ

  • これは最初からテストを書いた。はじめてのSwiftアプリで手探り状態のところ、テストコードが拠り所になった。
  • テストツールにはQuick*8を使用。
  • Nimble*9というMatcherライブラリを使えば、assert()を、expect().to()形式で書けて可読性が高い。
    • Arrayなども直接比較できる。拡張もできるので、Snapshot用のMatcherを作った。

QA

  • TDDはハイコスト。クライアント側は手を抜いていいのでは?という意見があるが -> Swiftは型が強力なのでLLと比べてテストの重要性は低いが、でもコンパイラだけではチェックできない不具合を見つけられる。ドキュメントの価値もある。テスト大事。
  • Snapshot Testはどのテストレベルでやっている? -> 決めかねている。End to Endでなくてもいい。
  • TDDで「機能」を捉えるのが難しい -> TDDは賛否両論。Ashさんはどちらでもなくケース・バイ・ケース。繰り返しやっているとテスタブルなコードを書けるようになる。ロバストにはなりきらなくても、価値はある。小さく、小分けして、DI。繰り返しやってみること。練習である。

QAのメモはかなり怪しい。@niwatakoさんの書き起こしも参照。

niwatako.hatenablog.jp

所感

テストに対する姿勢、メリットが明確で、かつ4パターンの実際のアプリの事例を挙げられていることもあって、とても説得力のある、良いセッションでした。Spec BDD派が増えそう。

Spec BDDに限らず、可読性の高いテストはドキュメントとしても有効なので、ちゃんとメンテナンス、リファクタリングすべきですね。

また、質疑応答で出たTDDの話。プロダクトコードとテストコードを行き来することで、複数の視点でコードを見ることができるようになるはずなので、訓練だと思ってTDDをやってみるのは良いことだと思う。 後からテストを書くのは難易度が高いことが多いので、テストを書く習慣、テスタブルなコードを書く習慣をつけるのは良いこと。

カンファレンス全体を通して

先日のDroidKaigiもそうですが、開発者主体で、スポンサーも付けて、有料で、海外からも参加者およびスピーカーが来てくれる。ほんの2〜3年前には考えられなかったことで、それを実現された主催者・関係者の尽力はすごいと思います。

改めて、お疲れ様でした。ありがとうございました。

こうして(ごく一部のセッションの)ブログを書くことくらいしかできませんが、少しでも盛り上がりに貢献して、次回以降につながればいいな、と。

最後に蛇足ながら。Twitterで「本カンファレンスが1スレッド進行なのが良かった」という話がありました。完全に同意!なのですが、それは今の時期のSwiftだから、という条件付きかも。マルチセッションがすべて悪、みたいな方向には行かないで欲しいかな。

*1:Javaでは「C言語のヘッダファイルみたいに増える」という批判がありましたが、少し前までObjective-Cで書いていたから抵抗ないはず?

*2:モック愛好家のポジショントーク注意

*3:日本だとトラックを使いますよね?

*4:いわゆる仕様化テスト

*5:と言っていたけど恐らくKiwiかな?

*6:describeと同義

*7:FacebookOSSとのことなので、恐らく https://github.com/facebook/ios-snapshot-test-case を使用

*8:RSpec, Kiwiと同様、Spec BDDライブラリ。 https://github.com/Quick/Quick

*9:https://github.com/Quick/Nimble

Mastering Android NDK(PACKT)の査読をした話

Packt Publishing Ltd. から出版された『Mastering Android NDK』の査読をお手伝いさせていただきましたのでご紹介。

f:id:nowsprinting:20151212122627p:plain

PACKTでeBook版を購入すると、PDF、ePub、Mobi*1でダウンロードできるほか、Kindleのコレクションに直接送る(send-to-kindle)こともできます。 Print版(ペーパーバック)もキレイな作りです。

www.packtpub.com

もしくは、Amazonでも購入できます。

www.amazon.co.jp

サンプルコードはPACKTのライブラリからダウンロードできますが、同じものがGitHubにもあります。

github.com

本書の内容・想定読者

本書は、Android NDKを使って、Android SDKだけでは実現困難なグラフィックスやサウンドの実装方法を紹介し、最終章でゲームを一本完成させる、という構成です。

Chapter 1でNDKアプリのビルド、Chapter 2でよく使われるC++ライブラリの組み込み方法を紹介した後、以降Chapter 10までC++の本。UnityやUnreal Engineといったゲームエンジンの台頭により、「クロスプラットフォームのゲームをAndroid NDKで作る」というケースは減っていると思いますが、もっと下のレイヤーまで知りたい、C++を駆使して実装したい、という方には参考になるはずです。

参考までに、目次は以下の通りです。

  • Chapter 1: Using Command-line Tools
  • Chapter 2: Native Libraries
  • Chapter 3: Networking
  • Chapter 4: Organizing a Virtual Filesystem
  • Chapter 5: Cross-platform Audio Streaming
  • Chapter 6: OpenGL ES 3.1 and Cross-platform Rendering
  • Chapter 7: Cross-platform UI and Input System
  • Chapter 8: Writing a Rendering Engine
  • Chapter 9: Implementing Game Logic
  • Chapter 10: Writing Asteroids Game

査読を頼まれた経緯

私のまわりでPACKTの査読をしたという話を聞いていないので、経緯などをざっくりと。

日本では、著者が自身の知り合いに査読を依頼し、その査読反映をもって著者脱稿として編集者に渡る、という流れが一般的だと思いますが、PACKTでは以下の流れで進められました。

  1. PACKTの「レビュアー獲得責任者」が、本書の査読ができそうな人を探して打診する。GitHubリポジトリなどの実績から選んでいるそうです
  2. 原稿は、著者とも編集者とも直接やり取りはせず、本書のコーディネータ経由で送られてくる(まとめてではなく、章ごとに数週間間隔で)
  3. 受け取った原稿に対し、テクニカルな指摘のほか、章ごとに内容の過不足や満足度といったアンケートも書いて返送する

本書に関しては、Android NDKやGradleまわりのサンプルをGitHubに上げていたのが目に止まったのだと思います*2。ニッチなものでも(ものこそ?)公開しておくものですね。

英語は義務教育レベルも怪しい私ですが、文法や言い回しに関してはちゃんとPACKTの編集者さんが付いており、我々レビュアーはテクニカルなところだけ見てくれればいい、ということでお受けしました。 とは言え、テクニカルなところも、オーディオとか門外漢なので余りお役に立てていないのですが。

なお、PACKTの書籍をお持ちの方はご存知だと思いますが、Reviewerは紹介文付きで載ります。

f:id:nowsprinting:20151212115359j:plain

「ぜひ書店でお手にとってー」と言えないのが洋書の辛いところですが、GitHubに上がっているサンプルコードを見て良さそうなら本も買う、という判断がいいように思います。

また、Print版を持っていますので、勉強会などでご一緒するときに事前に言って頂ければ持参し、お見せすることはできます。

*1:Kindle向けフォーマット

*2:噂のトニーモリス氏からはメール来てません

リンスタカフェvol8「ユーザーテスト見学会」に行ってきました #devlove

バイトルで有名なディップ株式会社さんで開催された、リンスタカフェ vol8 「特別編:ユーザーテスト見学会~みんなでピザとビールとUTを楽しもう!」に行ってきました。

devlove.doorkeeper.jp

実際に参加者の中から数名が被験者となって、写真共有サービス「30days Album」などのユーザテストを行ない、その様子をつまみに飲むという趣向。したがって、まず乾杯して開始。

ちなみに「リンスタカフェ」とはリーン・スタートアップに関するアレコレを話たりするイベントで、今回はなぜか初参加の方が多かったとのこと(私もです)。

以下、進行に沿ったメモですが、あとから質問した内容なども追記しています。

LEDライトを点灯させるテスト

  • ウォーミングアップとして、5名選出
  • 一人づつ室内に呼び、点灯のさせかたが特殊なLEDライトを渡し「点灯させてください」(タスクを与える)
  • 試行錯誤する様をみんなで観察
  • 点灯できたら、試行錯誤の手順を振り返り、「どう考えて操作したか」をヒアリング(回顧法/retrospective report method)
  • プレッシャーがかかるので、失敗しやすい
    • プレッシャーを緩和させる努力は、やっても効果が薄い
    • さらにプレッシャーをかけることもある。制限時間を設けて、砂時計を置く。

回顧法と思考発話法

  • 回顧法は、被験者が思考を覚えていないので不完全
  • 思考発話法(think aloud method)は、少し練習させてからでないと難しい
    • 練習は、お手本の動画を見せるのが早い。例えばUIscopeさんの被験者向けお手本動画(下記)
    • 被験者曰く「それでも難しい。行動を話すのが精一杯」

www.youtube.com

写真共有サービス「30days Album」のテスト

  • 被験者は、以下を満たした方
    • iPhoneを常用している方(Android版はUIが異なるため、OS自体への慣れ)
    • 普段から写真のシェアを行なう方
    • 30days Albumについて知らない、聞いたこともない方
  • 時間の関係で2名でしたが、本来は5名いれば問題の85%は見つけ出せるとのこと。
  • 今回は思考発話法なので、別室で練習。
  • ユーザーテストの流れ
    • 事前アンケート。普段どのように写真を共有しているか。誰に、どのサービスで。
    • タスクの提示(複数のタスクもあらかじめ提示。小出しにしない)
    • タスク(思考発話法)
      • 友達からアルバム共有のURLが送られてきたので、アプリで開く
      • アルバムから写真を数点、端末に保存する
      • アルバムに自分で撮った写真を追加する
      • 新規にアルバムを作り、写真を追加して友達にシェアする(SNS等でシェアする手前まででゴール)
    • 事後ヒアリング。このアプリを今後も使おうと思ったか、どこで詰まったか。

ドキュメント共有「DocBase」のテスト

  • 被験者は、以下を満たした方
    • 開発者(開発者向けサービスなので)
    • DocBaseを使ったことがない方
  • 同様に思考発話法で実施
    • 配属された先でDocBaseを使っている。アカウント追加したメールが届いたので、サインインして開発環境セットアップ手順書を確認する
    • 日報のテンプレートがあるので、探して日報を書く

所感

ユーザーテストの実施方法と、それを実際に見ることで、悪いUI/UXを炙り出せることは実感できました。

ただ、ではやろう、となったときの課題として以下がありそうです。

  • 適切な被験者、適切なタスクの設定
  • 結果をどう受け止めるか。シンプルに悪く、修正できる問題ならいいけれど、往々にしてトレードオフがあったりするはず

前者は、UIscopeさんのようなユーザーテスト実施サービスを利用することで解決しそう。タスクなど自前で用意できるのであれば安く、そうでなければテスト設計から分析レポートまで付けてくれるプランもあるそうです。

ユーザーテストサービスのUIscope

一方後者は、特に受託でやっていると色々難しい気がしました。受託の場合、顧客主導で(顧客 - UIscope 間で)やってもらわないとうまくいかなそう。

また、今回は触れられませんでしたが、テストとして実施するのであれば合格基準を設ける必要がありますし(例えばタスク完遂率とか、NE比とか)、このあたりはISO 9241-11とかISO 13407とか読んでみなくては。

あと、以前おすすめされて積読になっていたこれを読まなければ。

マーケティング/商品企画のための ユーザーインタビューの教科書 (Mynavi Advanced Library)

マーケティング/商品企画のための ユーザーインタビューの教科書 (Mynavi Advanced Library)

クックパッドエンジニアのトークナイト 〜クックパッドテストエンジニアのあり方〜 に行ってきました #cooketn

クックパッド株式会社さんで開催された『クックパッドエンジニアのトークナイト 〜クックパッドテストエンジニアのあり方〜』に行ってきたメモ。

connpass.com

クックパッドテストエンジニアのあり方

松尾さんのスライドに沿って、要所要所で t_wadaさん、諸橋さん、yoshioriさん含めたパネルディスカッションが入る進行。

www.slideshare.net

以下、メモ。

  • クックパッドでは、"Quality Improvement"と位置づけられている。品質"保証"ではない
  • 呼び方:QA、テストエンジニア、テスター、etc...
  • 募集から見てみると、やはり様々
    • Test Engineer @Google
    • Senior Software Engineer @Netflix
    • SWET @DeNA
    • テストエンジニア @cookpad
  • Qualityとは?
    • 「品質とは誰かにとっての価値である」
    • あなたのビジネスは何を生業としていますか?
      • 自動車など、製造業:腐食など経年劣化まで含めて保証 -> Quality Assuranceという考え方
      • 受託開発:納品のため。合意形成した指標を満足する。また、それを示す。 -> これもQuality Assuranceでok
      • サービス業:利用時の品質、ISO 25000, ISO 9241, ITILなど。対人なので「保証」はそぐわないのでは?
        • 価値検証を「しっかり素早く」し、改善を繰り返して品質を「向上」させる
          • TDD/BDD、自動化されたテスト、内部品質を高める
  • TDDと品質
    • 5年くらい前に炎上した話題。5年経過した今は?
    • t_wadaさん:
      • 厳密なTDDをやってる人は少ないけど、自動テストを書いている人は増えている、という印象。業界/会社ごとに乖離している。
      • 炎上の原因。「テスト駆動開発」の「テスト」という言葉。testでなくchenkだよねという話。「テストとは対象を破壊しようとする試み」(マイヤーズ『ソフトウェアテスト技法』)を前提とすると、TDDのテストとはcheckingである。
      • 開発者テストは書く習慣がつくと、なかなかやめない。
      • 割り切って考えられるエンジニアが増えた。「それはTDDじゃない」みたいなTDDの定義の話でなく、自分のスタイルでテストは書く、という方向。
  • サービスの開発速度を高めるために補完されるテスト
  • テストエンジニアに求めているもの
    • t_wadaさん:テスト書きすぎ問題。適切に減らすのが難しい。どこまでテストするのか
      • see: 直交表、ペアワイズ法。ツールとして、PICT、PictMaster
    • 諸橋さん:不具合のパターン、ユーザのイレギュラー操作を突くテストケース
  • サービスの開発速度を高めるための組織
    • 専業化していく? Quality Engineer, Software Engineer in Test, Test Engineer, etc...
    • クックパッドでは、今:土台作り、テスト環境の改善。発展させていきたい

ソフトウェア・テストの技法 第2版

ソフトウェア・テストの技法 第2版

  • 作者: J.マイヤーズ,M.トーマス,T.バジェット,C.サンドラー,Glenford J. Myers,Todd M. Thomas,Tom Badgett,Corey Sandler,長尾真,松尾正信
  • 出版社/メーカー: 近代科学社
  • 発売日: 2006/08
  • メディア: 単行本
  • 購入: 7人 クリック: 267回
  • この商品を含むブログ (46件) を見る

質疑応答

  • テストエンジニアを募集したけど集まらなかった
    • テストエンジニアはそもそも少ない
    • 欲しいスキルセット、応募側のスキルセットの解離があるのでは? GoogleやNetflixのように、ちゃんと名前をつけるべきでは?
    • 海外/外資だとQAの地位が高い。知識も高い。まず松尾さんが高給取りになるべき
  • テストコードがないプロジェクト
    • まず自分だけでもテストを書く。徐々にまわりに広げる
    • 先にCIサーバを立てて、静的解析やインスペクションだけでもまわす。周りに、面白そう、楽そう、という印象を与えて広める
  • テストの過不足、十分性
    • 足りている状態を定義、どこをokのラインとするか。正常系を優先とか。リスクの高低で評価する
    • 組み合わせテストも、組み合わせ数の十分性がレポートとして出ているので基準にできる。判断根拠の資料は残すべき。
  • End to Endテストは誰が書くべきか
    • 諸橋さん:E2Eのシナリオも開発者が書く限りchecking。テストエンジニアによってtestingに持っていく
    • yoshioriさん:black box/white boxで考える。E2Eはblack boxで中身を知らない人が書くべきなので、分業が理想
    • 松尾さん:checkingは自動で判断できるところまで。ユーザビリティなど人間がやるところがtestingという切り分け
    • t_wadaさん:気付かないものは気付けないので、それが開発者テストの限界。テストエンジニアが探索テストで見つけた不具合を自動テストで再現して自動テストに取り込んでいく。checking を拡張してtestingに。ランダム性、揺らぎを取り入れることで自動テストを拡張する

所感

特に「開発者テストをやってる人」「テストエンジニア」どちら向けともアナウンスされていないイベントでしたが、参加者も両方混在だったような雰囲気でした。

テストエンジニアに求められるものはどんどん高度化していく(けどまだ地位が低い)ので、うまく開発者側と補完しあえる組織の事例が増えていくといいですね。