ロコガイド テックブログ

「地域のくらしを、かしこく、たのしく」する、株式会社ロコガイドの技術部ブログです。主にトクバイ・ロコナビのサービス開発について発信しています。

「地域のくらしを、かしこく、たのしく」する、株式会社ロコガイドの技術部ブログです。
主にトクバイ・ロコナビのサービス開発について発信しています。

Androidアプリチームにテストコードを書く習慣ができるまで

こんなに長く自宅勤務が続くなら鉄のフライパンとか炊飯器とか買ってもよかったなーと思ってます。Androidアプリエンジニアのiaiaです。

前回Firebase Test LabでE2Eテストの話をしましたが、Unitテスト周りでも改善をしていて、良い変化が生まれてきたなと思ってきているので報告します。

入社直後のテストカバレッジ

私が出した最初のPull Request時点でのカバレッジです。

f:id:iaiaie:20200918104236p:plain

10%程度ですね...。

テストコードを書くのは大変で高いハードルがあるのは確かです。ですが、テストを書いていかないと、開発者は不安になりながらコードを変更しなければなりませんし、 何か修正が入るたびに面倒な手動テストを行わなければなりません。ユーザに不具合のあるアプリを提供してしまう可能性もあります。

安心してコードを変更し、リリースするためには、テストを書いてカバレッジを上げるしかありません。
そのためにテストを書く習慣をチームに根付かせることが必要だと考えました。 その習慣を根付かせるためには、テストを書くことへの高いハードルを取り除いていくしかありません。

「テスト書いて」と口だけで済ますのではなく、テストが書きやすい環境を作り、また、実際に自分がテストを書きまくる姿勢を見せることで、チームにテストを書く習慣を根付かせるのではないかと考えました。

テストを書きやすくするための環境づくり

まずテストが書きやすい環境づくりをしていきます。

これは単に自分のわがままなんですが、RSpec風にテストが書けるのが魅力のテストフレームワーク Spek でテストを書きたい!
JUnit4だと 「条件A かつ 条件B のとき 結果X になる」のようなテストをネストして書きづらいので*1、よりシンプルに書けるSpekを導入します。

新しい書き方になるので、チームでも使ってもらうためにこういうふうに書くと良いですよというwikiを書いて共有し、馴染みやすくしました。
結果、新しいテストフレームワークでも早く受け入れてもらえたので、Spekを導入したのはやはり正解でした。

TestRule も整備しており、一部残っているJUnitなテストではそのまま使っていますが、
SpekではTestRuleをそのまま使うことは出来ないので、 LifecycleAware の拡張関数で呼び出す、という手法を取っています。

fun LifecycleAware.lockLocalDateTime(
    year: Int, month: Int, dayOfMonth: Int, hour: Int, minute: Int, second: Int = 0
) {
    beforeGroup {
        LocalDateTimeTestRule.lockCurrentTime(
            LocalDateTimeTestRule.NowForSpek(year, month, dayOfMonth, hour, minute, second)
        )
    }

    afterGroup {
        LocalDateTimeTestRule.unlockCurrentTime()
    }
}


object HogeTest : Spek({

    lockLocalDateTime(2000, 1, 1, 0, 0)

    ....
}

心理面でのサポートも必要だと考えていて、テスト書かないとレビューで弾かれそう、と思われないよう「テストなんてなくてええんや...」とか、 必要であれば「私がテスト書くので...」と常々言うようにして、テストに対してプレッシャーを感じないように気をつけています。

この活動は今後も継続して行っていきますが、ある程度書きやすくなってきたので、実際にテストを書いていきます。

フォーカスしてテストを書く

広く浅くテストを追加しようとしても習慣を根付かせることは出来ません。

まずは一箇所にフォーカスして、その箇所の全てのクラスに対してテストを書き、 「XXXに関しては全部テストが書いてあるので、そこにクラスを追加したら必ずテストも書く」という習慣づけを狙います。 その習慣づけが成功すれば、別の箇所でも少しずつその習慣が発揮されて、全体としてカバレッジが上がっていくでしょう。

次に、どこにフォーカスして最初の一歩を踏み出すかを以下の条件で考えます。

  • 書くのにあまり時間がかからない
    • 習慣づけを早くしたい
    • そのクラス群の全てのクラスに対してテストを書くので時間は一定かかる
    • そのための最初の一歩で時間がかかりすぎないようにしたい
    • 複雑な機能のテストをやっていたんじゃ時間がかかる...
  • 変更があまり無い
    • テスト追加の作業に集中し、テスト修正の作業をなるべく避ける
  • ビジネス的に価値がある
    • あまり価値が無いところでテストを書くのはもったいない
  • 手動でQAしている
    • 価値がある・ミスをなくしたいから手動でQAしている
    • 自動化して効果を実感してもらう

これらを満たす機能で、かつテストがあまり書かれていないのが「ユーザの操作ログクラス」群でした。
これはパラメータを受け取ってJSONを生成し送信するだけの単純なクラスで、よほどのことがない限りは一度作った後に変更されることもありません。
さらに、追加や変更があるたびに、間違いが起きないように手動での目視確認を入念に行なっています。

最初の一歩を踏み出すにふさわしいクラスが見つかったので、実際にテストを書いていきます。

ログクラスのテスト実情

調べてみると用途ごとにログクラスが分かれており、これが80個ほどあります。
しかしこれらのクラスにテストが全てあるかというと...数個ある程度でした。

どのクラスも、

  • jsonに含まれていて欲しいkey-valueがあるかどうか
  • 送信しているかどうか

をassertionにかけるテストを書けば良さそうです。 また、最初の1個目のテストをしっかり書けば、あとはコピペしてexpectのjsonを変更するだけで済むので、かなり楽にカバレッジを上げられる気がします。オレはやるぜオレはやるぜ

オレはやったぜ

f:id:iaiaie:20200918104255p:plain

2カ月ほどかけて、業務の合間に、ひたすらコピペして、json書き直して、実行して、commitして、Pull Requestを上げて...を繰り返して完成しました。

その後

この活動のおかげか、少なくともログクラスに関しては全員が必ずテストを書くようになりました。

これは、自分がそういう活動をしているのを見ていたから、というのもあるかもしれませんが、何より他のクラスのテストをコピペして中身をちょこちょこっと変更するだけで終わるのが大きいと思っています。つまりログクラスのテストへのハードルは気づかないほど低くなっています

また、チームメンバーがログに限らずめちゃくちゃテスト書いてくれるようになってきました。
冒頭の「チームにテストを書く習慣を根付かせる」という目標を達成出来たと思います。
この目標を達成できたのは、自分の活動を快く受け入れて実践してくれる、素晴らしいメンバーがいたからです。

素晴らしいメンバーと一緒に働きませんか?

その後のカバレッジ

気になるカバレッジですが、10%近く向上しています。

f:id:iaiaie:20200918104244p:plain

まだまだ理想よりは低いですが、このような取り組みを継続し、テストを書くのが当たり前な環境にしたいです。

おまけ

色々偉そうに書いたが、じゃあ現在の自分がどうかというと...

テスト全然書いてない!
プロトタイピングのときのテストな〜書きづらいな〜捨てるかもしれんしすぐに変更あるしな〜

*1:Enclosed で条件をネストさせてテストを書くこともできるが...