ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

android.casual.test #2 に参加しました & LT しました

android.casual.test とは

android.casual.test は、Android のテストについてカジュアルに語るイベントです。 2 回目の開催となる今回は、4 月 3 日に行われました。

私は去年の 5 月ごろから Android アプリ開発の勉強を始めたのですが、なかなか社外の Android アプリ開発者と交流する機会がなく、交流したいなーという気持ちが高まっていたので参加してきました。

関西でもこういう感じのイベント (勉強会) をやりたいですね。

LT: はてなにおける Android アプリのソフトウェアテスト

私は 「はてなにおける Android アプリのソフトウェアテスト」 というタイトルで LT してきました。

はてなでは、最近 (ここ数ヶ月ぐらい) Android アプリのソフトウェアテストを行うようになりました。 どういう環境でテストを動かしているのかや、どういうテストを書くようにしているのかということを紹介し、また、テストを書く中で得られたノウハウなども紹介しました。 上のスライドで紹介しているのは、あくまで Gradle を使う場合の基本 (だと私は思ってます) のテスト手法です。 より実践的には、@rejasupotaro さんが紹介されていた RoboSpock を使うなどの方法もありますが、「まだテストを書いたことがないけどテストを書いた方が良さそう」 と感じている人 (あるいは組織) は、まずは導入として Android 端末 (実機 or エミュレータ) 上で動くオーソドックスなテストを書くところから始めてみると良いのではないでしょうか。

Android アプリのテストについての基本的な部分は、次の書籍も参考になります。

Androidアプリテスト技法

Androidアプリテスト技法

あくまで基本的な部分は、です。 初心者向けの本なのである程度テストを書ける人が読んでも得られるものは少ないでしょう。 次の感想記事も参考にしてください。

各 LT 感想

android.casual.test #2 での各 LT の感想です。

JUnit テストを 1 日やってみた」

@amyu_san さんの LT。

生まれて 20 年で初めてテストを書いた、ということだったけど、個人開発者で 「テストを書こう」 という意識を持ってるのがすごいなーと思った。

Gradle とかビルドシステム周りのことをあまり知らなくても IntelliJ でテストを書けるっぽく (ちゃんとわかってない) て、新しい発見 (気付き?) だった。 そういえば通常の Java プロジェクトを Eclipse で書くときは、GUI 上でぽちぽち操作してテスト用のクラスを作ったりテスト実行したりしてるので、Android アプリ開発でもできて当然という気はする。

「ノット・ジャバ・テスト」

@rejasupotaro さんによる LT。 「テストを書き続けられる環境を作ろう」 という話や、RobolectricSpockRoboSpock などの紹介。

今のうちの会社だと 「自然とテストを書けるような雰囲気 (?)」 みたいなのができていると思うけど、人が変わったり状況が変わったりするとテストを書くのが苦しいような状況にもなり得るので、テストを書くのが難しくない仕組みや環境づくりについてはいつも意識しておく必要があるなーと思った。

あと Spock というものや RoboSpock というものを初めて知った。 また今度使ってみよう。

「もしもの時にも安心な uiautomator の watcher 機能」

id:sumio_tym さんの LT。 uiautomator 自体は知っていたけど使ったことがないので興味深かった。 uiautomator は、他者が作ったアプリの UI テストもできるらしくて、アプリをまたぐような操作シナリオのテストなどに重宝するとのこと。

テスト中に NullPointerException が発生してエラーダイアログが表示された場合、(その挙動がテストシナリオで想定されていないものであれば) 後続のテストが全て失敗する、というようなことが起こるので、UiWatcher を使って監視する、という感じの話。 uiautomator を使うようなテストを書くことってなかなかないけど、今度使うようなことがあれば UiWatcher 周りも検討しようと思う。

「テストツール導入しようとしている話」

id:androhi さんの LT。 UI テストを実施するために Robolectric や各種 Gradle プラグインを試したりしたけどうまくいかず、結局 Espresso に落ち着いた、という話。

rejasupotaro さんもブログ記事に書かれてるけど、UI 周りのテストってどこまで自動化するか難しいなー、という気がする。 今のところ、個人的には Espresso を使った単体テスト (?) 程度に留めておくのが吉かなー、と思ってる。 (もちろん条件次第だけど、私が関わってる Android アプリでは、という感じ。)

「テストプラットフォームサービスの舞台裏」

@_touchy_ さんによる LT。 Android 端末の実機上での動作確認やテストをリモートで行うことができるサービスである Scirocco Cloud の舞台裏ということで、実機の運用に特有の苦労話などが紹介された。 実機の運用という全然知らない世界の話だったので、純粋に面白かった。

お疲れ様でした!

LT でも懇親会でもいろいろな話が聞けて有意義でした。 参加者の皆様お疲れ様でした!

Gradle プラグイン vc-android-sdk-manager 0.1.0 をリリースしました

リポジトリ

Git プロジェクトを GitHub 上に置いています。 Maven リポジトリGitHub 上に置いてあります。

このプラグインの目的

Android プロジェクトの Gradle ビルドスクリプトから Android SDKandroid update sdk を実行することです。

通常、既存の Android プロジェクトの開発に携わるとき、最初に次のようなことを行う必要があります。

  1. Android SDK (と IDE など) をダウンロードしてくる。 (まだしていない場合。)
  2. 既存の Android プロジェクトを手元に持ってくる。 (git clone とか。)
  3. Android SDK アップデートを行い、プロジェクトで必要なコンポーネントをダウンロードする。 (← これ)
  4. 開発を始める。

上の流れの中の、「これ」 と書かれた部分を自動化します。 (ビルドスクリプトの実行時に行うようにする。)

こういう問題を解決します

  • 手元でビルドする場合: Android SDK をインストールした後、Android SDK Manager でどのコンポーネントをインストールすればいいのかわかりづらい。
  • Jenkins で CI している場合: build.gradle の設定を書きかえた後、Jenkins 上でコンポーネントのインストールやバージョンアップをしないと Jenkins 上でのビルドに失敗するようになってしまう。

使い方

GitHub 上に Maven リポジトリを展開していますので、次のようにビルドスクリプトの依存を記述すれば使用できるようになります。 これはルートプロジェクトにのみ記述すればよいです。

buildscript {
    repositories {
        maven {
            url 'https://raw.githubusercontent.com/nobuoka/vc-gradle-android-sdk-manager/mvn-staging/m2repo/'
        }
    }
    dependencies {
        classpath 'info.vividcode.android.build:android-sdk-manager:0.1.+'
    }
}

次に、プラグインを使用するプロジェクトにプラグインを適用します。

apply plugin: 'vc-android-sdk-manager'

そうすると、そのプロジェクトでは androidSdkManager という変数が使えるようになります。 androidSdkManagerupdateSdkComponents メソッドや updateSdkPlatformAndBuildToolsAfterEvaluate メソッドを持っていて、次のように実行することで、Android SDKコンポーネントのインストールや更新を行います。 (まだ最新のリビジョンがインストールされていない場合。)

// このメソッド呼び出し時に、指定のコンポーネントをインストールする。
// コンポーネントの指定は、android update sdk コマンドの filter オプションに指定する値。
androidSdkManager.updateSdkComponents(['tools', 'platform-tools', 'extra-android-m2repository'])
// プロジェクトの評価後に 'build-tools-XXX' と 'android-XXX' をインストールする。
// プロジェクトの `android` extension に設定された値を見て、どのバージョンをインストールするか決定する。
androidSdkManager.updateSdkPlatformAndBuildToolsAfterEvaluate()

また、上のように実行すると、インストール時のライセンスに同意するかどうかの入力はユーザーに委ねますが、次のように acceptLicenseAutomatically オプションを使うと、自動でライセンスに同意します。 自動でライセンスに同意しますので、使用する際には自身の責任でもって使用してください。

androidSdkManager.updateSdkComponents(['tools', 'platform-tools'],
        acceptLicenseAutomatically: true)
androidSdkManager.updateSdkPlatformAndBuildToolsAfterEvaluate(
        acceptLicenseAutomatically: true)

必要な場合にのみ実行するようにする

Android SDK の更新は毎回のビルド時にする必要はないので、次のように指定の Gradle プロパティがある場合にのみ実行するようにすれば良いでしょう。

// `setupAndroidSdk` プロパティがある場合にのみ SDK の更新を行う。
if (project.hasProperty('setupAndroidSdk')) {
    apply plugin: 'vc-android-sdk-manager'
    androidSdkManager.updateSdkComponents(['tools', 'platform-tools'])
    androidSdkManager.updateSdkPlatformAndBuildToolsAfterEvaluate()
}

上のようにビルドスクリプトに書いておくと、通常時は Android SDK の更新は実行されず、次のように setupAndroidSdk プロパティを設定したときには Android SDK の更新が実行されます。

./gradlew -PsetupAndroidSdk assembleDebug

内部でやっていること

内部的には次のことを行います。

  1. android list sdk --extended を実行し、指定されたコンポーネントが含まれているかどうか確認する。
    • 含まれているもののみをカンマで連結し、文字列 filters 生成。
  2. android update sdk --no-ui --all --filter filters を実行し、コンポーネントをインストール。

ただし、build-tools に関しては、未インストールでも android list sdk の出力に現れないため、インストール先ディレクトリが存在するかどうかを見てインストール済みかどうか判別しています。

(android コマンドは Android SDK の tools ディレクトリに入っているものです。)

類似のプロジェクト

Cookpad による類似のプロジェクトがあります。

読んだ: CODE COMPLETE 第 2 版 上 — 完全なプログラミングを目指して

『CODE COMPLETE 第 2 版 上』 を読み終えました。

CODE COMPLETE 第2版 上

CODE COMPLETE 第2版 上

ソフトウェア開発の中の、詳細設計やコーディングとデバッグ単体テストなどの部分 (本書では 「コンストラクション」 と呼んでいる) に焦点を当てた書籍です。 プログラミングを行う際に気を付けるべきことがまとまった良い書籍です。 気を付けて当然のプラクティスばかり紹介されているという意味では常識集という感じでもあります。 プログラミングを初めて 1 年目や 2 年目の人が読むといろいろ学びがあるでしょう。

(追記) 下巻も読みました。 感想は下記記事。

内容紹介

上巻は 4 部構成です。

第 1 部 基礎を固める

第 1 部は、コンストラクションの中身に入るまでの準備などの話です。

まず第 1 章で、「ソフトウェアコンストラクション」 が表す範囲が、コーディングやデバッグ、詳細設計、統合といった 「実際にソフトウェアを作る部分」 であるという話や、それらについて論じることの重要性が述べられたりします。 上流工程が大事なのはそうですが、コンストラクションの過程も同様に大事ってことです。

2 章では、ソフトウェア開発においてメタファ (たとえ) を使用することが理解の促進につながることもあれば、悪いメタファを使用することで無用な混乱を招くこともある、という話がされます。 また、ソフトウェアコンストラクションのメタファとして、入念な準備が必要であることや、小規模なものと大規模なものの違いを理解しやすいということなどから、「建物の建築」 が適しているだろうと書かれています。 メタファに関しては、下手なメタファを用いることで混乱の要因になっている例が多々ありますので、良いメタファを用いることができるように気を付けたいですね。

3 章では、コンストラクションに入る前の準備 (上流工程) について論じられます。 アジャイル開発などの反復型の開発手法もありますが、どのような開発手法を採ろうともコンストラクションの前の準備は必要であり、課題定義や要求の策定といったことを適切に行わないと、コンストラクションの際に解決する課題を誤ったり、コンストラクション時の要求変更 (それはコンストラクション前に起こるよりもコストが大きい) が発生しやすくなったりします。 また、コンストラクションの準備として、アーキテクチャの設計も十分に行う必要があるとも書かれています。

4 章は 『コンストラクションの重要な決断』 ということで、プログラミングに使用する言語の選定や、プログラミング規約を定める、といった話が書かれています。 また、興味深い話としては、「プログラミング言語中で」 のプログラミングをするのではなく、「プログラミング言語中へ」 のプログラミングをせよ、というものがありました。

  • 「言語の中で」 のプログラミングというのは、使用している言語やツールがサポートしている構造に縛られて考えるということ。
  • 「言語の中へ」 のプログラミングというのは、(言語やツールに縛られない) 自分の考えを表現するために、使用している言語やツールでどのように実現するか決める、ということ。

わかりやすい例としては、「プライベートメソッドをサポートしていない言語を使っているときには、プライベートメソッドという概念を使用しない」 というのが 「中で」 のプログラミングで、「プライベートメソッドをサポートしていない言語では、メソッド名の命名規則でプライベートかどうかを表すことにする」 というのが 「中へ」 のプログラミングです。

第 2 部 高品質なコードの作成

第 2 部は、設計からクラス、ルーチンといった粒度での議論がなされます。 ここら辺の話を論じている本は他にもいろいろありますので、プログラミングのプラクティスがまとまっている本を読んだことがある人なら大体知ってる内容だと思います。

5 章はコンストラクションにおける設計の話です。 鉄則として、複雑さに対処するということが述べられ、単純さにこだわることが重要視されています。 モジュール間の依存関係を整理することは重要ですし、結合を疎にすることと凝縮度を大きくすることも重要です。 当然ですね。 設計はヒューリスティックな作業で、繰り返し行うことで設計が洗練されていく、ということが書かれていて、確かにそうだなー、という思いました。 (時間をかけたら設計ってよくなると思ってる。 もともとの能力次第だけど。) 特に価値の高い概念として、情報隠ぺいが紹介されていました。

6 章はクラスの作成についてで、クラスインターフェイスは一貫性のある抽象化を実現せよとか、「is a」 関係のモデリングを除き、通常は継承よりも包含の方が望ましいとか、そういう感じの話です。

7 章はルーチン (関数とかプロシージャ) の話です。 ルーチンの凝縮度を適切にせよ (1 つのルーチンで複数の機能を実現するのは多くの場合適切でない)、とか、名前を適切につけよ、とかですね。

8 章 『防御的プログラミング』 では、「無効な値が入力されたときに (通常の処理を行って) 無効な値を出力する」 というようなことをしないように入力値のチェックをするとか、例外処理を適切にするとか、そういう話がされます。 開発時には無効な値が検出されたらそれとわかるように大げさなエラーになるようにしておいてデバッグしやすくし、製品版では無効な入力に対してユーザーに適切なメッセージを出すように変更する、とか。

9 章では、擬似コードを書いてから実際のコードを書く、という手法 (擬似コードプログラミングプロセス; PPP) が紹介されました。 コードに落とし込む前に処理の流れを自然言語で記述することで、自分でも理解しやすく、他者にもレビューしてもらいやすい処理の流れを記述することができるなどの利点があります。 確かに複雑な処理だとこういう手法を採るのも良さそうですね。

第 3 部 変数

第 3 部では、変数について論じられます。 例えば以下のような感じです。

  • 変数のスコープをできるだけ狭くする。
  • 1 つの変数を複数の目的で使いまわさない。
  • 良い名前を付ける。 解読しやすく、目的がわかりやすいように。 8 から 20 文字程度の長さが良さそう。
  • 命名規則を上手に使う。 (全部大文字だと定数、とか。)
  • マジックナンバーを使わない。 (代わりに名前付き定数を使う。)
  • 必要がない限りグローバル変数を使わない。

ここら辺は、当たり前のこととしてやってくれって感じですよね。 他には、構造体やポインタのはまりどころを避けるためのプラクティスなどが書かれています。

第 4 部 ステートメント

第 4 部では、文について論じられます。 例えば以下のような感じです。

  • if 文などの条件が複雑になるようなら、ブール関数でカプセル化する。
  • 必要であればループの途中にループの出口を作る。 (A → B → A → B → A という感じの処理が必要な場合は、while (true) { A; if (...) break; B; } みたいなコードにする。)
  • goto 文はむやみに使うべきではないが、場合によっては良いものである。
  • 深いネストはルーチンの理解を妨げるので、ネストは浅くすべき。
  • 論理式を単純で読みやすいものにすること。

上には一部しか書いてませんが、第 3 部と同じで基本的には当たり前のこととしてコーディング時に気を付けてくれって感じのことがほとんどですね。

あと興味深いこととしては、18 章で述べられている 「テーブル駆動方式」 があります。 テーブル駆動方式というのは、論理文を使ってデータを取得したり処理の内容を決めたりする代わりに、テーブル (配列やマップ) を使って情報を参照するというものです。 具体的には、変数 month に何月かを表す数値 (1 月が 0 とする) が入っている場合に、その月の日数を得るために

int numDaysInMonth;
if (month == 0) {
    numDaysInMonth = 31;
} else if (month == 1) {
    ...

とするのではなく、

// 予め用意されているテーブル
int[] numDaysInMonthTable = { 31, 28, 31, ... };

int numDaysInMonth = numDaysInMonthTable[month];

みたいな感じでデータを取得する、というものです。 必要に応じて使う手ではありますが、こういう本で紹介されてるのが面白いなーと思いました。

感想

プログラマが経験的に得るであろう知見が良くまとまってるなーと思いました。 まともな環境で育った数年目のプログラマ (あるいはソフトウェアエンジニア) にとっては当たり前のことばかりなので読んでも面白くないと思いますが、1 年目から 2 年目にかけての新人が読むと得るものは多いと思います。 是非読みましょう。

数年目の人間でもここら辺の基礎がなってないプログラマも結構居ますので、そういう人間にも是非読んで欲しいですね。 (読ませたいですね。)

CODE COMPLETE 第2版 上

CODE COMPLETE 第2版 上

値段が高くて個人ではなかなか手が出しづらいですし、「そのうち買おう」 と思っているうちに経験を積んで本書の内容では物足りなくなってしまうような気がするので、チームに 1 冊とか会社に 1 冊とか置いてあると嬉しい気がします。

内容プレビュー

本書の内容の一部が web 上で公開されていますので、どういう内容なのか興味がある方は見てみると良いでしょう。

読んだ: レガシーコード改善ガイド

Web 上でそこそこ評価が高いようですね。

自分はリファクタリング経験が結構あった (テストを書くことで保護して変更する) ので本書から得られるものはそこまで多くはなかったのですが、テストを書くとかリファクタリングの経験がそんなにない人は本書から得られるものは多いと思います。 これまでテストを書いたことがほとんどないとか、テストしやすい設計について考えたことがないというようなソフトウェアエンジニアはぜひ読んでみてください! 普段からテストを書いたりしてるけど、テストがないコードを変更するという経験をしたことが少ない人にも参考になるかと思います。

内容紹介

本書では、「レガシーコードとは、単にテストのないコードです」 と述べられています。

第 1 部は、既存のソフトウェアを変更する際の理由を 4 つ (要件追加、バグ修正、リファクタリング、最適化) に分類したり、レガシーコードを変更する際にテストで保護することがいかに有効であるか、単体テストを行いやすくするために何を考慮すべきか、といったこと、そして、リファクタリング単体テストに有効なツール (IDE とかテストハーネスとか) の紹介がされます。

第 2 部が本書の主たる部分で、レガシーコードを変更する際の 「具体的な問題設定」 と、それに対する 「具体的な解決策」 がそれぞれの章に書かれています。 安全に変更するためにはテストで保護する必要がある、という主張のもと、いかにして依存関係を排除してユニットテストを書くか、ということが述べられていたり、テストでの保護が難しい場合に、変更箇所以外に影響が及ばないように変更するにはどうすればよいか、というようなことが書かれています。

例えば、6 章 「時間がないのに変更しなければなりません」 では、既存のシステムに追加された要件を満たすように短時間でシステムを変更する必要があるが、(既存のメソッドに変更を加えた場合に) 追加された要件以外の部分に影響がないことを確認することが困難である、という状況に対する解決案が述べられています。 他の章も、章の表題自体がソフトウェア開発者が直面しがちな問題を表していて、どの章でどういう問題について述べられているかすぐにわかるでしょう。

対象となっている言語は、主に C、C++JavaC# です。

第 3 部は 25 章 「依存関係を排除する手法」 のみからなり、第 2 部の説明の中で使われた依存関係を排除するためのリファクタリング手法がカタログ形式で掲載されています。

感想

全体として、

  • テストしづらいコードをどういう風に変更すればテストしやすくなるのか、
  • 依存関係を排除するためにどうすればいいのか、
  • 理解しづらいコードを理解するためにどういう手段を取ると良いか、

ということが、著者の経験を基に述べられています。 内容的にはさほど高度なものはなく、ちゃんとテストを書きながら保守開発を行うということをしていれば得られる知見が文書としてまとめられている、という感じです。 そういう意味では、経験のあるソフトウェアエンジニアが読んだところで得られるものはほとんどないでしょう。

一方で、そのような知見が 1 冊の本としてまとめられているというのは価値あることで、保守開発の経験が少ない人は読むといろいろ得られるものがあるでしょう。 実際の開発時に本書で述べられている手法を使うかどうかはともかくとして、本書で述べられているような手法があることはソフトウェア開発者であれば知っておくべきだと思いますし、本書に書かれている内容を知識として持っていないソフトウェアエンジニアには、是非本書を読んでほしいと思います。

「Typetalk Hack Kyoto」 に参加しました

「Typetalk Hack Kyoto」 という nulab 主催のイベントに参加しました。

イベントの様子は上の記事にまとめられています。

Typetalk とは

nulab が開発しているチャットツールで、2 月に正式リリースされたもの。 基本はビジネス向けではあるけれど、「いいね!」 を付けられるなど、楽しい要素も盛り込んでいるらしい。

Typetalk Hack Kyoto

内容は @tksmd さんによる Typetalk の話とハッカソン (実際に Typetalk の API を使って何か作る)。

tksmd さんは 『開発現場に伝えたい 10 のこと』 の著者の 1 人。 tksmd さんが書かれた部分は nulab のブログで公開されている。

『早く行きたいなら、一人で行け。遠くに行きたいなら、みんなで行け。』 というアフリカの諺が紹介されていて、なるほどー、という感じ。

Typetalk の API について

Web からもスマートフォンアプリからも、基本的に同じ API を使用するようになっている。 また、製品で使用している API は、基本的にサードパーティに公開しているとのこと。 その理由などは次のエントリで書かれている。

Web 側とスマートフォンアプリ側での違いとしては、認証方式が違うということがある。 あと、一部 API (管理系 API) は web 側でしか使用していなくて、サードパーティへの公開もされていないらしいけれど、将来的には公開する予定とのこと。

開発者が Typetalk API を使う場合は、認証には OAuth 2 が使用できる。 次の 2 つの方式がサポートされている。

  • Client Credentials: 単一ユーザーで使う場合 (バッチ処理とか)
  • Authorization Code: 複数ユーザーに提供する場合

API についての雑感

Typetalk の API を使うのは、(HTTP 通信さえできれば) 結構簡単にできる感じだった。 テスト完了時にテスト結果を通知するボットを作るとか、わりと手軽にできると思う。

OAuth 2 を使ったことは今まであんまりないのだけれど、OAuth 2 は使いやすくて良いなー、と思った。 サービス提供側としても OAuth 2 のことをちゃんと調べておきたい。 nulab ではサーバーサイドの OAuth 2 実装として次の Scala プロジェクトが公開されているので、参考にできそう。

それと、統一された API を提供し、web からもアプリからもそれを使用する、という設計方針について。 サーバーサイドのメンテナンスコストが小さくなるし、こういうツール系の web サービスについては Ajax で (クライアントサイドで) 動的に画面を生成するというのは一つの選択肢として良さそう。 とはいえクライアントサイドが複雑になるので、そこら辺は難しいところだなーと感じた。 *1

書いたもの

自分は Groovy で Typetalk に通知メッセージを送るようなボットを作ろうと思ったけど、Groovy で HTTP 通信するのに慣れてなかったから API を叩く簡単な処理ぐらいしか公開できるものはできなかった。 Gist で公開しているので、Typetalk の API を使う処理を Groovy で書く際の参考にどうぞ。

Groovy での HTTP 通信

せっかくなので少し。

Groovy の HTTP 通信用のライブラリとして、HTTP Builder というものがある。

Apache HttpClient のラッパーなのだけれど、Groovy で使いやすいような API になっていたり、組み込みで XMLJSON の変換処理が行われたりするのが便利。

非同期での HTTP 通信などもやりやすそうな感じ。

*1:個人的にはクライアントサイドの JS のメンテナンスはサーバーサイドのコードのメンテナンスよりも難しいと思ってる (サーバーサイドがどんな感じかによっても違うけど、一般的にはそうかなーと思ってる)。