Infinito Nirone 7

白羽の矢を刺すスタイル

軽井沢ワーケーションをしてきた

12/13~12/16にかけて星野リゾート BEB5軽井沢でワーケーションをしてきました。

BEB5軽井沢

初日の日曜日は移動日で、車を借りて軽井沢まで行きました。シーズンとしては雪が降ってもおかしくないところを走るため、レンタカーもスタッドレスタイヤのオプションをつけて借りました。

BEB5軽井沢はこんなかんじ。いい雰囲気ですね。

f:id:KeithYokoma:20201217212530j:plain

f:id:KeithYokoma:20201213180406j:plain

f:id:KeithYokoma:20201214184517j:plain

建物は2階建てで、1Fには受付のほかに居室とカフェ、いくつかの作業スペース、2Fには居室と自販機や洗濯機コーナーなどがあります。 また中庭にはこたつや簡易的な暖炉があり、冬でなければ外で作業もできそうです(冬はとても寒いし少しでも水たまりがあればカチカチに凍る)。暖炉を囲んで会話をすれば Fireside Chat が成立します。

日曜日は流石に人も多めではありましたが、それでも3密を避けるため多くのスペースが確保されており、かなり広々としていました。

今回の居室には2段ベッドがあり、下はソファー兼ベッド、上は2つのベッドがあります。下のソファーで一つの電源、上の2つのベッドもそれぞれに電源と読書灯がついています。

f:id:KeithYokoma:20201213174007j:plain。・。

ハルニレテラス

BEB5軽井沢から北へ5分くらい歩いたところにある商業施設で、パン屋さんやカフェ、レストランなどがあります。パン屋さんとカフェは朝早くから開いているので、朝の散歩ついでに朝食をとりに出かけるのにちょうどよいです。

f:id:KeithYokoma:20201215191503j:plain

f:id:KeithYokoma:20201217234307j:plain

f:id:KeithYokoma:20201217234321j:plain

初日はイタリアンレストランで食事をしました。地産地消ということで、軽井沢やその近辺で育った野菜とワインをいただきました。

f:id:KeithYokoma:20201217234338j:plain

f:id:KeithYokoma:20201217234353j:plain

f:id:KeithYokoma:20201217234405j:plain

ハルニレテラスからさらに少し北に進むと、ステーキ屋さんや温泉があります。

f:id:KeithYokoma:20201217235558j:plain

f:id:KeithYokoma:20201217235615j:plain

f:id:KeithYokoma:20201217235638j:plain

温泉の泉質は草津に近い感じで、もしかすると肌が弱いとすこしヒリヒリするかもしれません(自分には不快ではないもののすこしヒリつく感じがありました)。 またサウナ完備で、コロナ禍の情勢を踏まえソーシャルディスタンスが確保できるよう一度にサウナに入れる人数が制限されています。水風呂は非常に冷たく、一度入れば外気温が氷点下でもなんとなく暖かく感じます。

f:id:KeithYokoma:20201214215353j:plain

ハルニレテラスから温泉まで、ひととおり星野リゾートが開発しているのでおなじ WiFi アクセスポイントでインターネットに繋がります。

ワーク

使うテーブルにもよりますが、カフェ側の長テーブルにはすべての席に電源があります。WiFi の速度もいい具合に出るので、一通り仕事がしやすい環境になっています。ワーケーションとして実際に仕事をしたのは月曜と火曜の2日間でしたが、そのどちらも自分用の電源が確保できる長テーブルで仕事ができました。ミーティングなどで機密性の高い会話をする必要があるときは居室にもどってミーティングに参加するといった使い分けが可能です。ただし居室でミーティングに参加するにはすこし明るさが足らず、特に居室のソファーではだいぶ暗いので、もうすこし明るさを確保できる位置取りをしたほうがいいかもしれません。

f:id:KeithYokoma:20201215103303j:plain

長テーブルもソーシャルディスタンスに配慮し隣の席との距離が確保されています。かなり広々と使えるので、多少荷物が多くても気になることはありません。むしろ、ランチなどでホテルから出るときに荷物を一気にその場でまとめられるので、機動力があがります。

今回は車での移動を主としたので、ランチもいろいろなところに出かけられました。 BEB5軽井沢自体は中軽井沢駅に近いのですが、軽井沢駅も車でそう遠くないので、軽井沢駅周辺のランチスポットで食事をとることが多かったです。 ちなみにBEB5軽井沢から旧軽井沢駅近辺のランチスポットに向かうと、いい具合にラウンドアバウトを通れたり、軽く雪化粧した白樺の木々を眺められる山道を通ったりすることができます。

f:id:KeithYokoma:20201214135055j:plain

f:id:KeithYokoma:20201215132205j:plain

その他

中軽井沢には近くにツルヤというスーパーマーケットがあります。ここはプライベートブランド商品が充実しており、かつ地元企業との協力により様々な商品を取り扱っています。 ビールはヤッホーブルーイング、コーヒーは丸山珈琲、それ以外にもドライフルーツは長野県産の果物を使っていたり、ワインも長野のワイナリーでつくったものを扱っていたりと、地元に根ざしつつとても美味しい食品をいくつも作っています。 お惣菜も充実していて、なにより安い!ツルヤが近所にほしいと切実に思いました。

最終日は軽井沢のアウトレットの南にある西武のホテルでビュッフェランチをしました。平日ということもあり、非常にひろいビュッフェで片手で数えられる組のグループが食事をしていました。こちらも長野の食材を使用した料理がおおく、デザートも小布施牧場の牛乳を使ったジェラートが楽しめるなど、とにかく地元推しで美味しい食事が楽しめる最高のリモートワーク環境でした。また行きたいどころか住むのもありなのではと思うほどでした。

おわりに

軽井沢には新幹線でもいけますが、BEB5軽井沢に宿泊してワーケーションをするなら車で行ったほうがよさそうです。なにより、食事の選択肢が広がります。東京のようにあらゆるものが24時間営業とはいかず、コンビニでさえも深夜は閉まるくらいですが、それが特に気にならないくらい他のスーパーやレストランが充実しています。 また BEB5軽井沢での宿泊の特典として、温泉の利用料金が平日無料、休日は半額となる点もとても良いです。気分を変えたいときに散歩以外に温泉に浸かる選択肢もできるので、キンキンに冷えた水風呂につかってから全集中の呼吸へ移行することもできます。

非日常での仕事は楽しくかつ適度に集中ができ、環境の整った自宅とは違った進捗を出すことができます。

季節としては夏は避暑、春や秋も涼しく、冬は寒いですが豪雪地帯ではないので気軽にワーケーションをするにはもってこいの場所のようです。なにより食事が美味しい。この記事も気がつけば食事の画像がほとんどでした。しかしご飯が美味しいのはとても重要で、いろんな食事を楽しめたからこそなおのことワーケーションが充実したように感じます。

劇場版 SHIROBAKO のエンディング

この記事は SHIROBAKO ADVENT CALENDAR 2020 10 日目の記事です。

とにかく劇場版 SHIROBAKO のエンディングが素敵なのだ。多分にネタバレを含むので、まだ劇場版を見ていない方はいますぐ U-NEXT にサインアップして見てほしい。何ならテレビ放送の分まで配信されているので、それをみてから劇場版を見てほしい。今なら初月無料(劇場版 SHIROBAKO は課金が必要だけど 1 週間見放題)だし(一応申し添えておくと、自分は U-NEXT の回し者でもなんでもない)。

続きを読む

自宅に STF (Smartphone Test Farm) を立てて検証端末を管理する

コロナ禍のなかで検証用の Android 端末をどう調達・管理するのかという話題を DroidKaigi.fm #4 でしてきました。その会話の中で、自分の場合は検証端末を全部自宅に持っていて、STF を使ってつないでいるという話をしたのでそのあたりを少し掘り下げてみようと思います。

STF とは

Smartphone Test Farm という OSS ソフトウェアで、これをつかうことで Web ブラウザから STF サーバにつながっている Android 端末を操作できるようになります。adb による接続もできるので、端末を自分の PC に USB ケーブルでつなぐことなくアプリのデバッグが可能です。

github.com

本来は社内ネットワーク内などで STF サーバを立てておき、社内から誰でもブラウザで端末を使えるようにするようなユースケースで使うものですが、自分の場合は自分で会社の検証端末の管理もすることになったので、半ばファイルサーバにしかなっていなかった Mac mini に STF サーバを立て、これを中心に検証端末の管理をしつつ、いつでも使いたい端末をケーブルの抜き差しの手間なく使えるようにしました。

STF を立てる

STF の README にある通りの手順で STF サーバを立てます。事前に Requirements にあるものを準備しておきますが、Node.js が 8.x であることに注意する (これより新しいバージョンでは動かないものがあるよう) 以外は、Installation の手順通りでよいはずです。

検証端末を STF サーバに接続する

開発者オプションの USB デバッグが ON になっていれば、USB ケーブルで STF サーバにつなぐだけで STF が自動で端末を認識してブラウザから使えるようにしてくれます。

一方で、端末を管理するうえで問題になるのは、端末を繋ぎっぱなしにしておくとバッテリーの寿命が早まってしまうことです。とくに検証端末はいつでも使えるようにしておきたいので、STF サーバに繋ぎっぱなしにしておきたいところですが、そうするとずっと充電状態となってしまいます。USB ハブのなかには、電源タップのように ON/OFF スイッチのついたものがあり、これをつかって適度に ON/OFF を切り替えてできる限りバッテリーに負担のないようにしたいところですが、USB の口すべてにスイッチが付いているのは面倒です。そこで自宅ではできる限りたくさん USB の口を確保しつつ、ON/OFF スイッチは1つで全体をコントロールするものとしてこの USB ハブを使っています。

検証端末の保管場所をつくる

1台や2台程度ならどうということもないですが、5台以上になってくると、そのへんに積んでおくわけにはいかなくなってきます。今回は東急ハンズで仕入れた木材を適当に加工して簡単なスマホラックを作りました。

ひとつのラックで3台ほど格納できるように木材をあわせます。下からケーブルを通して差し込めるようにしてあります。

f:id:KeithYokoma:20201120184530j:plain

これを4個並べて運用しています。

f:id:KeithYokoma:20201120184541j:plain

kotest 4.3.0 で Robolectric Extension が安定版になった

以前に kotest を使ったテストに Robolectric をあわせるときの注意点を書きましたが、このときはまだ Experimental な状態だった Robolectric Extension が 4.3.0 から安定版となりました。

blog.keithyokoma.dev

マイグレーション作業

安定版となったことで次にあげるマイグレーション作業が必要になります。

import 文の修正

RobolectricExtension および @RobolectricTest のパッケージが変わっています。

- import io.kotest.experimental.robolectric.RobolectricExtension
+ import io.kotest.extensions.robolectric.RobolectricExtension
- import io.kotest.experimental.robolectric.RobolectricExtension
+ import io.kotest.extensions.robolectric.RobolectricExtension

安定版リリースでの変更点

同一モジュール内での @RobolectricTest と通常のテストの共存

以前は同一モジュールで RobolectricExtension を有効化すると、そのモジュール内のすべてのテストで @RobolectricTest が必要でした。安定版では必要なテストクラスのみに @RobolectricTest を付与し、@RobolectricTest の有無に関わらずすべてのテストが正常に実行できるようになりました。

安定版リリースでも動作しないもの

BehaviorSpec が利用できない

以前と変わらず、Robolectric の初期化が二重に走ってしまいテストがコケます。

IsolationMode を InstancePerLeaf 以外にするとテストがコケる

以前と変わらず、InstancePerLeaf 以外では Looper の初期化が二重に走ってしまいテストがコケます。

Navigation Component で Build Variant ごとに Deep Link を定義する

Build Variant ごとに Deep Link (App Links) の host を変えたいとき、Navigation Component の navigation graph の定義をどうするかを示します。

<deepLink> では String Resources が使えない

navigation graph も XML で記述したリソースだと考えると、次のような定義は一見正しそうですが、生成される AndroidManifest を見ると全く予期しない結果になります。

<navigation>

  <fragment>

    <deepLink app:uri="@string/deep_link_uri" />

  </fragment>

</navigation>

{build_variant}/res/values/strings.xml に deep_link_uri を定義すれば、Build Variant ごとに Deep Link の URI が切り替えられそうですよね。上記の navigation graph から生成される AndroidManifest の intent-filter は次のとおりです。

<intent-filter>

  <action android:name="android.intent.action.VIEW" />

  <category android:name="android.intent.category.DEFAULT" />

  <category android:name="android.intent.category.BROWSABLE" />

  <data android:scheme="http" />

  <data android:scheme="https" />

  <data android:host="string" />

  <data android:path="/deep_link_uri" />

</intent-filter>

String Resource への参照そのものが URI だと解釈され、string という host で /deep_link_uri という path の URI で Deep Link を設定してしまっています。 Issue Tracker にこの問題の報告がありますが、どうやら <deepLink> での String Resource への参照はサポートしていないようです。代わりに、Manifest Placeholder のような仕組みを導入する予定があるようです。

navigation graph を Build Variant ごとに定義する

仕方がないので navigation graph の XML を Build Variant ごとに定義するしかありません。

navigation graph が十分にシンプルで小さいなら navigation graph 全体を Build Variant ごとに配置してもよいかもしれませんが、Deep Link の設定を切り分けるのであれば、Deep Link を持つ <fragment> だけを切り出した navigation graph を Build Variant ごとに用意し、それを main source set にある navigation graph から <include> するほうが良さそうです。

Android アプリのモジュールで kotest を使いユニットテストを記述する

以前は Kotlintest と呼ばれたテストフレームワークが Kotest と名前を変えて開発が進められています。

github.com

このテストフレームワークは非常に多様なテストの記述方法をサポートしており、テストケースのスタイルからアサーションメソッド、パラメトライズドテストのサポートまでテストに必要な幅広い関心をカバーしています。 マルチプラットフォームでの動作をサポートしたテストフレームワークとなっていて、自分で拡張を導入するための構造も持っています。公式にも様々な拡張が提供されていて、Robolectric と一緒にテストを動作させるための拡張も用意されています。 ただし2020年10月現在 Robolectric の拡張は experimental であり、うまく動作しないパターンが多々あります。この記事では、次に示す各ライブラリのバージョンを組み合わせて kotest を Android アプリプロジェクトに導入する方法を紹介します。

  • kotest: 4.2.5
  • kotest-robolectric-extension: 4.0.1
  • Robolectric: 4.4

依存関係

build.gradle に下記の記述を加え、kotest および Robolectric を導入します。

android{
  testOptions {
    unitTests.all {
      useJUnitPlatform()
    }
  }
}

dependencies {
  // kotest
  testImplementation 'io.kotest:kotest-runner-junit5:4.2.5'
  testImplementation 'io.kotest:kotest-assertions-core:4.2.5'
  testImplementation 'io.kotest:kotest-property:4.2.5'
  testImplementation 'io.kotest:kotest-extensions-robolectric-jvm:4.0.1'

  // robolectric
  testImplementation 'org.robolectric:robolectric:4.4'
}

必要に応じて、テストの source directory 配下に resources ディレクトリを掘って、robolectric.properties ファイルを作ります。

sdk=28
application=your.app.pkg.YourApp

kotest-robolectric-extension による Robolectric の有効化

テストの source directory の好きなパッケージに ProjectConfig オブジェクトを作ります。

ProjectConfig オブジェクトは様々な extension を追加したり、テストの並列数を制御したりするためのオブジェクトです。 Robolectric を kotest で動作できるようにするには、extensions で RobolectricExtension を含むリストを返します。

また、Robolectric を使う場合の制約として、IsolationMode を InstancePerLeaf にしておく必要があります。IsolationMode はテストケースのインスタンスをどの単位でもつかを制御するパラメータで、InstancePerLeaf がもっとも細かい単位でインスタンスを作ります。現状の kotest robolectric extension による Robolectric の初期化処理では、モジュール内に複数のテストを記述したファイルがある場合、Looper の初期化処理が複数回呼ばれてしまい例外が投げられることがあります。IDE では個別にテストを実行するため気付きにくいですが、CLI で一気にテストを実行するとこのエラーに遭遇します。InstancePerLeaf ではテストケースのインスタンスごとに Robolectric の設定も異なる扱いをうけるためか、この初期化処理の重複による失敗を回避できます。

object ProjectConfig : AbstractProjectConfig() {

  override val isolationMode: IsolationMode = InstancePerLeaf

  override fun extensions(): List<Extension> = listOf(RobolectricExtension())
}

ちなみに、筆者が試した限り Robolectric を有効化したモジュールで ProjectConfig の parallelism を 2 以上にした場合も Robolectric の内部状態が不正になりテストが実行できなくなりました。

テストの記述

kotest-robolectric-extension で Robolectric を有効化したモジュールでは、自分が確認した限り次のテストスタイルが利用可能です。これら以外を使用すると Robolectric の初期化処理が複数回呼ばれてテストが失敗します(とくに BehaviorSpecDescribeSpecFeatureSpec)。

  • StringSpec
  • WordSpec
  • FunSpec

Robolectric を利用するテストには @RobolectricTest アノテーションを付けます。 が、現状の kotest-robolectric-extension では同じモジュール内で Robolectric を利用しなくてもよいテストを記述している場合でも、@RobolectricTest アノテーションを付けておかないとテストが実行できない(テストケースがひとつも実行されず正常終了する)ため、Robolectric が不要なテストにも @RobolectricTest アノテーションを付与することになります。

@RobolectricTest
class SomeTest : StringSpec() {
  init {
    "something" {
      // Given: Values
      val a = 0
      val b = 1

      // When: Subtract
      val subtracted = a - b

      // Then: subtracted value should be -1
      subtracted.shouldBe(-1)
    }
  }
}

マルチモジュールプロジェクトでの kotest の設定

先に紹介した ProjectConfig はモジュールごとに定義します。マルチモジュールな状況では kotest を使うすべてのモジュールで ProjectConfig オブジェクトを作る必要があります。少々面倒ではありますが、これにより、Robolectric を使うモジュールとそうでないモジュールを切り分けることができるため、Robolectric が必要なモジュールでは StringSpec を用い、そうでないモジュールでは BehaviorSpec を用いるなどのスタイルの使い分けがしやすくなります。

チームで育てる Android アプリ設計プロジェクト、始まります

Peaks さんで「チームで育てる Android アプリ設計」と題した執筆プロジェクトに @kgmyshinさんと取り組むことになりました。この記事はこのプロジェクトが発足するまでの経緯や、執筆する本に込めた思いを伝えられればと言うことで書いています。

チームとアーキテクチャを両輪で語る書籍

これまでの様々なアーキテクチャのあるべき論から少し離れてみて、どうやったらうまくチームでアーキテクチャを扱えるようになるかを、釘宮さんと自分のそれぞれで得た経験をベースに話していく書籍が「チームで育てる Android アプリ設計」です。 アーキテクチャは一度整えればそれで終わるわけではなく、プロダクトの成長やチームの成長とともに少しずつ形を変えていくものであるという考えのもとで、新規開発チームでの事例と、走り始めたあとの規模の大きなチームでの事例を紹介していく予定です。

チームで育てる Android アプリ設計

この書籍のタイトルに込めた思いとして、チーム全員の目線を合わせてアーキテクチャを考えていきたいという思いがあります。

当初よりチームとアーキテクチャを両輪でうまくまわしていくための書籍を書きたいという思いからこのプロジェクトが始まりました。そこから、プロダクトが成長していくごとにアーキテクチャに求められるものも変化し、その時々に応じて少しずつアーキテクチャも微妙に形を変えていくはずだよね、という議論があり、さらにチームそのものもその変化に合わせて成長をしていくといいよね、というところから、このタイトルへと落ち着いていきました。

本当はチームがアーキテクチャを育てるだけでなく、アーキテクチャの変化がチームを育てることもあり、そこからさらにチームがアーキテクチャを進化させていくようなループが作れると理想で、そのことをうまくタイトルで表現できると良かったのですが、ひとまずはチーム一丸となってアーキテクチャを形づくり継続的に整えていくことを伝えるために「チームで育てる Android アプリ設計」という名前になりました。 そしてこのチームが一丸となってアーキテクチャの形を作り続けていくという姿が「都市計画」のようなイメージを彷彿とさせたことから、表紙のイメージはまるでシムシティで都市を発展させていったときの様子を写したようなイメージにしています。

さいごに

この書籍では、特定のパターンのアーキテクチャがよいという話ではなく、チームやプロダクトの状況からうまくアーキテクチャを整え、ともに成長していくことにフォーカスした内容に仕上げていく予定です。 個人的にはテストしたいところがテストしやすければアーキテクチャはなんでもよいと思っているのですが、数多あるパターンの中からうまく時流や要求にあった形にで落とし込めそうなものを模索してきた経緯を文章にできれば、きっと他のいろいろなプロジェクト・チームでも役に立つだろうと思っています。 ぜひ、応援をよろしくおねがいします。

クラウドファンディングサイト

peaks.cc

よろしくおねがいします!