$30 off During Our Annual Pro Sale. View Details »

テストゼロからイチに進むための戦略と戦術

 テストゼロからイチに進むための戦略と戦術

Japan Java Users Group Cross Community Conferrence 2016 Spring

Yu Watanabe

May 25, 2016
Tweet

More Decks by Yu Watanabe

Other Decks in Technology

Transcript

  1. 1
    JJUG-CCC 2016 Spring
    #ccc_e4

    View Slide

  2. 2
    ストップウォッチ スタート確認
    プロローグ

    View Slide

  3. 3
    ● 渡辺 祐
    ● (株)ビズリーチ
    ● ビズリーチ
    エンジニアブログ
    ○ http://tech.dcube.io
    ● Twitter: @nabedge
    ● https://github.com/nabedge
    ● http://www.slideshare.net/nabedge
    ● http://mixer2.org
    ● http://nabedge.mixer2.org

    View Slide

  4. 4
    同僚の島本さんによる
    セッションもどうぞ
    「ビッグデータじゃなくても使える
    Spark☆Streaming」
    AB-6 17:00~17:50

    View Slide

  5. 今日の話には前フリがありまして
    5
    http://www.slideshare.net/sogdice/java8jjug-ccc-2015-fall

    View Slide

  6. 6

    View Slide

  7. 7

    View Slide

  8. 8
    全力で地雷原を駆け抜けたら、
    海が広がっていた。
    ただしテストコードは無い。
    繰り返す。テストは無かった。

    View Slide

  9. 9
    綺麗な海をテストで
    守ってくれる人を We are HIRING !
    http://www.bizreach.co.jp/recruit/

    View Slide

  10. テストゼロからイチに進むための
    戦略と戦術
    10
    JJUG-CCC 2016 Spring
    #ccc_e4

    View Slide

  11. タイトルはあのお方より拝借
    11

    View Slide

  12. ▸ 今日話さないこと
    ▹ TDD, テストファースト
    ▹ C0, C1, C2
    ▹ カバレッジ50%超えたらその後どうする?
    ▹ 例外処理のテストとか
    ▸ 話すこと
    ▹ 何を準備すべきか、そのコツ
    ▹ なにから始めるか
    ▹ その障害はなにか
    12

    View Slide

  13. 13

    View Slide

  14. 本に書いてあることを妄信しない。
    現実は現実。
    ケースバイケースでいいとこ取り。
    14

    View Slide

  15. 業界によっても話は違う
    ▸ 受託開発 (SIer)
    ▸ 業務パッケージ開発
    ▸ 組み込み系
    ▸ Webサービス
    15

    View Slide

  16. 一手ずつ指すしかない
    16
    Yet Another 式年遷宮 ...

    View Slide

  17. 一手目
    なんらかの道標を
    継続的に見られる状態にする
    17

    View Slide

  18. 18
    1. 自分たちがいまどこにいて
    2. あとどのくらいがんばれば
    3. あのあたりに行けるかもね。
    4. これをチーム全員が
    見れるようにする

    View Slide

  19. 19
    SonarQube 一択

    View Slide

  20. 20
    mvn
    clean
    jacoco-prepare-agent
    test
    sonar:sonar

    View Slide

  21. 21
    これでメトリクスの推移を見れる!!

    View Slide

  22. 22
    実は、ここまでたどり着くのは
    そう簡単ではない。

    View Slide

  23. 23
    Jenkins上でテストが
    まともに動くようにするためには
    テストクラス 本体クラス
    初期データ投入済みの
    キレイなデータ層(orモック)
    外部APIサービス
    (orそのモック)

    View Slide

  24. 24
    少し話が飛ぶ
    (...かのように見えます)

    View Slide

  25. 25
    ローカル開発環境の構築作業
    1. git clone
    2. vagrant up でOracleVBoxに仮想OSを起動し、
    RDB, 検索エンジン等をインストール
    3. DBFluteのreplace-schemaで
    テーブル構築&初期データINSERT
    4. バッチスクリプトで検索エンジンにデータ投入
    5. Mavenプロジェクトとして
    IDE(Eclipse/IntelliJ)にインポート
    6. ServerStarter.javaを起動

    View Slide

  26. 26
    Jenkins上でのmvn test
    1. git clone
    2. vagrant up で別EC2インスタンスを起動し
    RDB, 検索エンジン等をインストール
    3. DBFluteのreplace-schemaで
    テーブル構築&初期データINSERT
    4. バッチスクリプトで検索エンジンにデータ投入
    5. mvn jacoco-prepare-agent test sonar:sonar
    6. vagrant destroy (インスタンスは使い捨て)

    View Slide

  27. 27
    ポイント
    1. ローカル開発環境のデータ層を、
    仮想OSもろともvagrant upの
    一撃で作れるようにしてある。
    a. 初期データ投入も自動スクリプトあり。
    2. ならばそれをテスト自動実行の
    環境(Jenkins)にも使えばいい。
    a. 環境差分は仮想OSのIPアドレスくらい

    View Slide

  28. 28
    実際、さらに話が飛びますが

    View Slide

  29. 29
    テスト「だけ」が開発じゃない
    1. 機能の追加、変更、廃止
    2. バグの対応
    3. インフラ、フレームワークの
    メンテ
    4. セキュリティ的な対応
    5. 新人育成、新メンバーの
    立ち上がりサポート
    全体を
    テストで
    支える

    View Slide


  30. 30
    テストの自動化以外の
    シチュエーションでも使える手法や
    ツールをチョイスすることで、
    一石二鳥を狙うべき。

    View Slide

  31. 31
    将来構想(の一部)
    ▸ ローカル開発環境
    ▸ 結合テスト環境
    ▸ 社内β環境
    ▸ 本番環境
    すべて
    Docker
    コンテナ化
    インフラ用ansibleをローカル環境でも使えたら!?

    View Slide


  32. 32
    詳しくは
    「12−factor App」
    「開発 本番 一致」
    でググる。

    View Slide

  33. 33
    話をテストに戻します。

    View Slide

  34. 34
    テストのカバレッジってなんぞ?
    緑=テストが通過している
    赤=テストが通過してない
    黄色=テストが一部だけ通過
    このクラスに対する
    カバレッジは67%
    ➗ コード全体
    テストが
    通過した箇所

    View Slide

  35. 35
    本体コード 116 KStep
    テストコード 91,577 KStep
    本体に対して 787倍のテスト
    もちろんカバレッジ100%

    View Slide

  36. 36
    オライリー本「実践JUnit」より
    ▸ 14.4.1 望ましいカバレッジの値
    ▹ 「EclEmmaの開発者も含むほとんどの人々は、
    70%以下のカバレッジは不十分だと...」
    ▸ 14.4.3 カバレッジの意義
    ▹ カバレッジの値は単体ではほとんど意味がありま
    せん。重要なのは、値の増減の傾向です。カバ
    レッジの値を落とすことなく、徐々に上昇させてゆ
    くことを目指しましょう。

    View Slide

  37. 37
    ちょっと休憩
    1. 水を飲む
    2. 時間を確認(15~20分くらい?)

    View Slide

  38. 二手目
    便利プラグインやライブラリは
    積極的に導入する
    38

    View Slide

  39. 39
    hamcrestのmatcherも悪くは
    ないんだけど
    assertThat("hoge", is("hoge"));
    assertThat("hoge", is(not("HOGE")));
    assertThat("not null", is(notNullValue()));

    View Slide

  40. 40
    AssertJが便利!
    import static org.assertj.core.api.Assertions.*;
    assertThat("hoge").isEqualTo("hoge");
    assertThat("hoge").startsWith("h").endswith("e")
    assertThat("not null").isNotNull();

    View Slide

  41. 41
    IDEをメンテしよう
    ▸ EclEmma
    ▹ 手元のeclipseでカバレッジを見る
    ▸ Quick JUnit
    ▹ テストクラスとテスト対象クラスを行き来する
    ▹ ただしEclipse-luna/marsでは一部メニューが
    動かない!
    ▸ IntelliJ IDEAなら上の機能はだいたいデフォルト搭載。

    View Slide

  42. 三手目
    ゴミ掃除で分母を減らす
    42

    View Slide

  43. 43
    レガシーコード改善ガイド
    16.4 「使用していないコードを
    削除する」
    ▸ 邪魔以外の何者でもない。
    ▸ 古いコードが見たければ
    VCSから掘り起こせ。

    View Slide

  44. 44

    View Slide

  45. 45
    ▸ さすがに1.7万行消したら
    カバレッジ少し上昇。
    ▹ 後日加筆の補足:不要に残ったコメントアウトではない(その悪習は
    初めから無い)。うっかりコミットしてしまっていた不要な logファイルで
    もない。本当に丸ごと未使用のビジネスロジッククラス等だった。

    View Slide

  46. 四手目
    ありもののテストデータの
    存在を前提としてテストを書く
    46

    View Slide

  47. 47
    理想: レガシーコード改善ガイド
    2.1 「単体テストとは」
    次にあてはまるものは単体テストではない。
    1. データベースとやり取りする
    2. (以降 割愛)
    上記に該当するテストが悪いというわけではない。
    … … (以下、テストが遅くなりがちだからダメという
    話)

    View Slide

  48. 48
    開発現場の現実
    ▸ これでは ↓ お話にならない
    ▹ 「手順書通りにソースをIDEに
    インポートしてアプリを起動したら、データ
    がほとんど入ってないせいで、画面がま
    ともに動きません!」
    ▸ すべての機能を正確に動かせる
    初期データを一撃でINSERTする仕組み
    は(自動テスト云々に関わらず)必須

    View Slide

  49. 49
    ▸ DBFluteのreplace-schema機能を使う
    1. テーブルを全DROP
    2. CREATE TABLE …
    3. *.xlsに用意したデータをINSERT
    (日時情報は相対指定可能)
    ▸ 他のツールでも代替可能
    ▸ sql-maven-plugin
    ▸ dbunit-maven-plugin
    ▸ Gradle, 自作bashスクリプト

    View Slide

  50. 50
    正しいテストデータが常に存在する
    前提でテストを書いてもよいことにする
    @Test
    public void test_foo() throws Exception {
    User user = userService.find(5L);
    Result result = fooService.doBar(user);
    assertThat(result)........
    会員番号5番のuserはこのテストに必
    要なデータを全て持っている

    View Slide

  51. 51
    ただし基本は勉強したうえで
    用法用量を守って。
    @Test
    public void test_foo() throws Exception {
    User user = new User();
    user.setHoge = ...//テストに必要な値をその都度書く
    Result result = fooService.doBar(user);
    assertThat(result)........

    View Slide

  52. 五手目
    ところで、メールのテスト
    どうする?
    52

    View Slide

  53. 53
    そこそこ面倒
    ▸ ローカルマシンにpostfixとdovecotを入れる
    ▸ 共有マシンにpostfixとdovecotを入れる
    ▸ 開発環境では、SMTPではなく
    *.eml形式でファイルに出力する
    自作モッククラスに差し替える
    ▸ GreenMail, mock-javamail,
    subethaSMTP...などのライブラリを使う

    View Slide

  54. 54
    ● インストール
    ○ $ gem install mailcatcher
    ● 起動
    ○ $ mailcatcher
    ● 1025番でSMTP待ち受け
    ● 1080番でブラウザでメール閲覧
    ● portは起動引数で書き換え可

    View Slide

  55. 55
    メールの情報をjsonで返すAPI
    ▸ /messages … メッセージ一覧を取得
    ▸ /messages/:id.json
    ▸ /messages/:id.html
    ▸ /messages/:id.plain
    ▸ /messages/:id.source

    View Slide

  56. 56
    mailcatcherをvagrantで自動構築
    動作チェックと自動テストの両方で使う
    ▸ vagrant upで”gem install mailcatcher”
    ▸ アプリケーションの設定値を差し替え
    ▹ SMTPサーバのIPアドレスとport番号だけ
    ▸ 開発過程での目視での動作確認用途に使う。
    ▸ テストコードではmailcatcherのAPIで
    取得したjsonをアサートする。

    View Slide

  57. 六手目
    Selenium
    - E2Eテストでカバレッジも測る
    57

    View Slide

  58. 58
    Seleniumご存知ですよね
    @Test
    public void test_トップページ() throws Exception {
    WebDriver drv = new FireFoxDriver();
    WebElement element =
    drv.get(“http://localhost:8080/”);
    assertThat(element.findById(“title”).getText())
    .contains(“Hello World”)
    }

    View Slide

  59. 59
    不安定だし、メンテコスト大きめ
    なので、用量用法を守って。
    … とは言うものの

    View Slide

  60. 60
    絶対防衛ラインの存在
    ▸ ECサイト
    ▹ カートに入れる->入力,決済->注文完了メール
    ▸ ホテル予約サイト
    ▹ 予約ボタン -> 入力,決済 -> 予約完了メール
    ▸ 転職サイト
    ▹ 応募ボタン -> 入力 -> 応募がありましたメール

    View Slide

  61. 61
    1. 是非もなく継続的にやりたい結合テストを
    Seleniumで書く。
    2. 通常のユニットテストと同じ運用で
    継続的に実行できるようにする。
    3. とにかく必ずやるというのなら、
    ついでにカバレッジも取れるようにする。

    View Slide

  62. 62
    前提:アプリは組込Tomcatで起動
    public class APStarter {
    public static start() {
    Tomcat tomcat = new Tomcat();
    tomcat.start();
    ...
    public static void main(String args[] argv) {
    start();

    View Slide

  63. 63
    @BeforeClass メソッド
    APStarter.start(); // 起動
    @Test メソッド
    // WebDriverでアプリの画面にアクセス
    @AfterClass メソッド
    APStarter.close(); // 停止

    View Slide

  64. JVM
    64
    テストクラス
    テスト対象
    Seleniumでもカバレッジ測定
    JaCoCo-Agent
    Jenkins用マシンのOS
    Vagrant仮想
    OS
    1. vagrant up
    2. 初期データ投入
    3. Xvfb起動
    4. mvn
    prepare-agent
    test
    sonar:sonar
    HTTP
    start()

    View Slide

  65. JVM
    65
    テストクラス
    テスト対象
    普通のユニットテストのカバレッジ測定
    JaCoCo-Agent
    Jenkins用マシンのOS
    Vagrant仮想
    OS
    1. vagrant up
    2. 初期データ投入
    3. -
    4. mvn
    prepare-agent
    test
    sonar:sonar

    View Slide

  66. 66
    もしも多種類ブラウザでやるとしたら?
    JVM
    テストクラス
    テスト対象
    JaCoCo-Agent
    Jenkins用マシンのOS
    Vagrant仮想OS
    JVM
    テストクラス
    テスト対象
    JaCoCo-Agent
    Jenkins用マシンのOS
    Vagrant仮想OS
    JVM
    テストクラス
    テスト対象
    JaCoCo-Agent
    Jenkins用マシンのOS
    Vagrant仮想OS
    JVM
    テストクラス
    テスト対象
    JaCoCo-Agent
    Jenkins用マシンのOS
    Vagrant仮想OS

    View Slide

  67. 67
    ちょっと休憩
    1. 水を飲む
    2. 時間を確認(35分くらい?)

    View Slide

  68. 68
    七手目
    Jenkins上でのテストの
    定期ジョブ実行を止めたくなる
    自分自身との戦い

    View Slide

  69. 69
    事件発生
    ▸ Jenkinsがチャットルームに
    「テストが失敗しました...」をつぶやく
    ▸ あれっ?と思ってローカル環境で
    テストを実行すると全て成功する

    View Slide

  70. 70
    ログを追うにも
    ● 実際には200MByte前後。
    ● log4j.properties / logback.xml を
    長い間 整理していないツケ

    View Slide

  71. 71
    ▸ 手元では再現しない。
    ▹ AWS(EC2)でしか発生しない。
    ▸ たまにしか発生しない。
    ▸ Jenkinsがオオカミ少年化するのが
    嫌だからチャットへの投稿botを停止。
    ▸ 忙しくてしばらく停止しっぱなし
    ▸ 本当にバグってテストが失敗してても
    誰も気づいてない。

    View Slide

  72. 72
    巨大ログをよくよく目grepすると
    ==> default: Existing lock /var/run/yum.pid: another copy is
    running as pid 3744.
    ==> default: Another app is currently holding the yum lock;
    waiting for it to exit...
    ==> default: The other application is: yum
    ● テストではなくvagrant の段階 !
    ● yum install hogehoge でコケている。
    ● 他のyumが動いている??

    View Slide

  73. 73
    vagrant up
    AMI起動 (vagrant-aws-plugin)
    yum update ... 他のOS起動シーケンス
    sshデーモン開始
    vagrant provision
    yum install hogehoge
    yum lock
    微妙に時間が
    かかることがある
    テスト実行ジョブスタート

    View Slide

  74. 74
    ▸ テスト用EC2インスタンスを使い捨て
    しているからこそ発生する事象。
    ▸ つまらない原因でCIサーバでのテストが
    事実上止まってしまうことはありうる。
    ▸ わずらわしさに負けたらそこで
    試合終了。

    View Slide

  75. 75
    八手目
    教育、啓蒙

    View Slide

  76. 76
    ▸ 幸いなことに
    ▹ 否定的な感覚のメンバーは皆無
    ▹ テスト書かないと気持ち悪いという者も。
    ▸ いずれにせよスキルにバラつきはある。
    ▹ テストを書き慣れることが必要

    View Slide

  77. 77
    テストを書くタイミングだけは
    守ろう
    1. バグ対応のときは必ずテストを書く
    2. クラスの中の一部のメソッドを
    修正または追加した場合は、
    そのメソッドに対するテストだけでも書く。
    3. そのために private -> protected に
    変更するのはOK
    ※他にもいろいろあるけど上記はその一例です

    View Slide

  78. 78
    九手目
    金の弾丸

    View Slide

  79. 79
    富豪テストを支える基本環境
    Mac Book Pro
    3GHz Core i7
    16GB memory
    250GB SSD
    Jet Brains
    All Products Pack

    View Slide

  80. 80
    まとめ
    ● 時間を確認

    View Slide

  81. 81
    1. テストが無いコードはレガシーコードだ!
    2. テストを書こう。
    3. しかし現実の開発現場は、
    それがすべてではない。

    View Slide

  82. 82
    1. 自分の手元の開発環境で、アプリの実行に必
    要なミドルウェア群を
    一撃でインストールする仕組み
    (not 手順書)
    2. すべての機能を正確に動かせる
    初期データを一撃でINSERTする仕組み
    テスト書くのとは無関係に必須

    View Slide

  83. 83
    ならばそれらの自動化ツールを
    テストの実行にも活かして手間を省く
    1. vagrant/dockerでデータ層を作る
    2. DBFluteのreplace-schemaで
    テストデータ投入
    3. あるいは
    a. sql-maven-plugin
    b. dbunit-maven-plugin
    c. gradle関連でももちろんOK

    View Slide

  84. 84
    IDE(のplugin)依存は避ける
    1. それJenkins上で動かせるの?
    2. QuickJUnitがEclipse luna以降では...
    3. WTP, Sysdeo, RunJettyRunよりも
    組み込みtomcatでアプリコード化

    View Slide

  85. 85
    道標はあるほうがいい
    ▸ なにを、どこまでがんばれば、
    どうなりそうか?
    ▹ ステップ数
    ▹ テストカバレッジ
    ▹ テストの成功、失敗、スキップ
    ▹ 重複ステップ数
    ▸ 上記すべての過去の値との比較

    View Slide

  86. 86
    便利ライブラリは積極的に導入
    ▸ Javaライブラリ
    ▹ AssertJ
    ▹ “J”Mockito
    ▸ Javaライブラリ以外の方法もある
    ▹ mailcatcher

    View Slide

  87. 87
    言語やフレームワークは
    最新ですか?
    ▸ If文の分岐網羅を気にするよりも
    Java8 の Stream API で書き直して
    見通しを良くする方が建設的。
    ▸ Spring-testフレームワーク便利!
    ▹ ただしspring4.2以上

    View Slide

  88. 88
    ゴミ掃除をしよう
    ▸ もう使われていないコードのテストを
    がんばって書く悪夢
    ▸ Log4j.properties, pom.xml を
    メンテする。
    ▹ 不要なログ、無駄な依存関係は
    トラブルシューティングの邪魔

    View Slide

  89. 89
    Seleniumは用法用量を守って
    ▸ 絶対防衛ラインで使う
    ▸ やり方次第でカバレッジも採れる
    ▸ テスト実行環境の自動構築を徹底すれば、
    富豪テスティングなやり方も可能。
    ▹ スポットインスタンス安っ!

    View Slide

  90. 90
    ▸ テストの原則は
    ▹ 繰り返し可能である
    ▹ 独立している
    ▸ これを↑実行環境レベルの自動化で
    実現する方法「も」ある
    ▸ ただし実行スピードが犠牲に
    なりやすい。考えて使いわける。
    基本は基本で知っておこう

    View Slide

  91. 91
    ▸ 気をつけていても、思わぬ落とし穴で
    テストが不安定になって
    心が折れそうになることがある。
    負けないこと。

    View Slide

  92. 92
    テストの旅は続く
    エンジニア募集!
    https://www.bizreach.co.jp/recruit/

    View Slide

  93. テストゼロからイチに進むための
    戦略と戦術
    93
    To be continued...

    View Slide