Nds#24 単体テスト

28
今日から始める! 単体テストの自動化のススメ 2012/01/29 @nemuzuka 24 回 長岡 IT 開発者勉強会 単体テストしやすいモジュール設計の指針をつくってミタ ~カバレッジ 40% めざせ

Transcript of Nds#24 単体テスト

Page 1: Nds#24 単体テスト

今日から始める!単体テストの自動化のススメ

2012/01/29@nemuzuka

第 24 回長岡 IT開発者勉強会

単体テストしやすいモジュール設計の指針をつくってミタ     ~カバレッジ 40%めざせ

Page 2: Nds#24 単体テスト

自己紹介

● id:nemuzuka,@nemuzuka● 昨年の春、地元の長岡に帰ってきました!

● サーバサイドに Javaを使用したWebアプリケーションの開発を主にやっております

● 新潟は Javaやってる人少ない気がします

Page 3: Nds#24 単体テスト

みなさん、テストしてますか?

「お金」という対価を受け取るレベルのプログラムを提供する以上、

テストしないということは考えられません。

リリ|ス?

テストしないで

え|

までだよね|

自己満プログラム

許されるのは

テストしなくて

Page 4: Nds#24 単体テスト

テストは1回やればおしまい?システムは生き物。

機能を追加していく過程でそもそもの振る舞いを壊してしまうこともしばしば。

ソフトウェアの場合は関係ないと思ってた箇所に思いもよらず影響を及ぼすこともあります。

開発言語 /DBサーバ /フレームワークのバージョン UPを行う際にデグレってないかの確認を全部手動でやるのはかったるい。だからといって、構築した時のバージョンを維持するってのも・・・ねぇ

やっぱり複数回やることが多いんです

Page 5: Nds#24 単体テスト

テストはしなきゃいけないのはわかるけど面倒なんだよなー

Page 6: Nds#24 単体テスト

機械の力を借りましょう。

そりゃ、毎回手動でおこなってたんじゃ効率が悪い。

Page 7: Nds#24 単体テスト

そこで今日のお題は

テストなんてかったるくてやってられな 限りある時間を有効に使いたいそんなあなたに、うってつけ!

Page 8: Nds#24 単体テスト

単体テストを自動化しやすいモジュール設計

について考えてミタテストを考える会

Page 9: Nds#24 単体テスト

テストの自動化というと

xUnitを使えばいいよねあとよろしく

Page 10: Nds#24 単体テスト

失敗 ENDへのフラグ

Page 11: Nds#24 単体テスト

ある程度の方針が必要なんです

● テストしやすい設計、アーキテクチャの確立

● 開発者個人にまかせてしまうと、粒度はバラバラ

(あ、これ俺のスキーマでないと動かないっスよ)

● 何やってるテストなのかわからない

(でも、カバレッジ高いから良いでしょ?)

● 作られたは良いけど、結局放置

テストが通らなくなってくると、魔法のパラメータ

「 -Dmaven.test.skip=true」でテストの実行を無かったことに

作ったものの、使い続けられないテストクラスが量産されることに・・・

Page 12: Nds#24 単体テスト

せっかくつくったのに

モッタイナイ

Page 13: Nds#24 単体テスト

まずは前提条件対象:MVCモデルを採用したWebアプリ

【思想】・ View/Controller

→   ロジック記述量を少なく (Actionにロジック書く奴はおしおき )・Model

→   もう、嫌って程集約

【自動化の対象範囲】

・ Ui部分 (View)は自動化対象外→   seleniumとか使ってもいいけど、 UI部分って変更多いし、

   かけた時間に対してメリットが少ないと思うの・サーブレット (Controller)も自動化対象外

→   いいからModelに全部任せておけって!   Mockでリクエストパラメータとか作るの面倒じゃん。

→   でも、 Sessionオブジェクトとか HttpRequestとか    J2EE前提なオブジェクトはModelに渡すなよ。   パラメータは POJOだ (簡単にインスタンスを生成できる奴でお願いします )

→   Modelからの結果をそのままレスポンスに設定するくらいの勢いで!

Page 14: Nds#24 単体テスト

頑張って自動化する箇所はModel

画面に表示する項目の加工すらもModelに持たせるとかして、 View部分にはできるだけ判断文を持たせないようにした方が幸せになれる気がします。(ロジックの分散を避けるのが目的です。 )

システム全体を自動化するには膨大な時間と工数がかかるので、対象を絞りましょう→   バグり易い所 (Model)を重点的にリグレッションテストしやすいように

   した方が費用対効果高そう。→   Webブラウザからの打鍵テストを行う時の確認事項も少なくなるはず。→   本来、Webブラウザの打鍵から始まる一連の処理は結合テストだと思う。

Page 15: Nds#24 単体テスト

自動テストしやすいメソッド

「このメソッドを呼んだら、結果どうなる」が容易に定義できるもの

もっと言えば、パラメータだけで処理して結果を返すメソッド

DBアクセスの場合は、前提条件のデータを格納しておいて、メソッド呼び出しで SQL発行させて結果確認、というのが基本的な流れだから、まぁとっつきやすい

Page 16: Nds#24 単体テスト

例えばこんな疑問

● xUnitのサンプルって、単純な処理(足し算とか引き算)ばっかであんまり実務っぽくないんだもん

– 現実のシステムはそんなに単純じゃないよ

– 1回のメソッド呼び出しでいろんなことやるじゃない

でもそれは、メソッドの振る舞いを一連の処理としてみたときの話

Page 17: Nds#24 単体テスト

粒度を小さくすれば

「結果どうなる」が定義しやすくなるんじゃね?

Page 18: Nds#24 単体テスト

ファイルのインポート処理アップロードされたファイルを1行ずつ読み込んで、テーブルに格納されていなければ、登録。格納されていれば、更新

public void doImport(InputStream is) { Readerインスタンスを生成 while(1行読み込む) { 読み込んだ情報を元に検索条件作成 DB検索 if(該当レコードあり) { 更新データを作成 DB更新 } else { 登録データを作成 DB登録 } } Readerをクローズ}

このメソッドを呼び出したらどうなるべきか??

テストケースのパターンを増やす時は、 InputStreamのパターンを増やす?

Page 19: Nds#24 単体テスト

こうしたらどうかな?public void doImport(InputStream is) { Readerインスタンスを生成 while(1行読み込む) { create検索条件 do検索 create登録・更新Entity do永続化 } Readerをクローズ}

検索条件 create検索条件(行データ) { XXXX}

検索結果 do検索(検索条件) { (DBアクセス)}

登録・更新Entity create登録・更新Entity(検索結果) { if(検索結果あり) { 更新用のEntity生成 } else { 登録用のEntity生成 }}

void do永続化(登録・更新Entity) { if(登録) { 登録処理(DBアクセス) } else { 更新処理(DBアクセス) }}

メソッド内の処理を細分化→  メソッドが何をしているのか

  わかりやすくする (メソッド間も疎結合 )→  テストパターンを追加する時は

  引数のパターンを増やす

※ソースコード中にコメントを書くよりも細分化単位で JavaDocを書いた方がよっぽどいいソースコードになる、と思う

Page 20: Nds#24 単体テスト

さらに一手間

● 細分化したメソッドのスコープはパッケージプライベート

– privateだとテストクラスから参照できない● わざわざリフレクション使うのもなぁ・・・

● publicでなければ大体良いんじゃね?● テストクラスは同じパッケージに配置

– どのクラスのテストをしているのかわかりやすく

– クラス名も XXXTestとかね。

Page 21: Nds#24 単体テスト

Mockオブジェクト

● 使わなくても良いように実装を見直してみる

● MockはあくまでもMock– テストクラス以外にもメンテナンスコストがかかる

– Mockを作ることに情熱をかけ始めたら本末転倒

● テスト済みであれば、実クラスを呼び出しても全然アリだと思う

Page 22: Nds#24 単体テスト

実装設計にもうひと手間

● Modelを責務に分けたレイヤーで細分化

Service

Logic

Dao DB

Model

※トランザクションスクリプトでもいいじゃない

Page 23: Nds#24 単体テスト

レイヤーの責務● Dao

– DBアクセス● 1テーブルに対する CRUD● 複数テーブルを結合した SELECT文発行

● Logic

– DAOに対するアクセスのとりまとめ● 親子関係のテーブルに対する登録処理

– Web/Batch共通で使えそうな処理● メール送信

● 複数 Serviceで利用される処理● Service

– Controllerとの間を取り持つ delegate– Logicに対するアクセスの取りまとめ

Page 24: Nds#24 単体テスト

テスト自動化のメリット● リグレッションテスト

● カバレッジレポート

– テストクラスで実行した箇所がわかる

● Jenkins等の CIツールと組み合わせて– 最新のソースコード取得

– ビルド (リリースモジュールの生成 )– テスト

– レポート出力

を自動で行うことができる

・プロジェクトは進んでいるか・「俺、仕事している」感を体感できるハズ

開発者の環境以外でも動作するようにテストクラスを記述することで、環境依存を減らす(テストクラスにおけるフルパス指定禁止、スキーマは CIツールや開発者毎 )

Page 25: Nds#24 単体テスト

老婆心ながら・・・● カバレッジの目標はがんばりすぎない

– ≠高カバレッジ 高品質

● そもそも仕様を満たしていないかもしれません

– カバレッジ 100%にするのは難しい● 外部リソースアクセス

● 標準 APIの例外– カバレッジでなく、 assertにこだわりましょう– SQLのカバレッジは取れない

● SQLでがんばっている箇所があれば、データパターンを増やしてテストもがんばる!

● バグが発生したら

– 再現するテストケースを書いて、そこから修正

– 抜けてた観点をチーム、組織で共有できれば尚良し

● 単体テストはあくまでもメソッドとしての振る舞い

– 結合テスト、システムテストが不要な訳ではない

Page 26: Nds#24 単体テスト

最後に

テストのスキルは、言語がかわってもそんなに変わりません。なにより、テストしやすい実装ができるということは、開発者として大きなアドバンテージがあると思います。

「テストしやすい実装=シンプルな実装」を目指せるはずです。システムは生き物なのでシンプルに保てれば保守もしやすくなるでしょう。

テストがいくら完璧でも、使う人に価値が無いと思われれば、そのシステムは意味がありません。テストだけに情熱をかけすぎないように、ほどほどに。

Page 27: Nds#24 単体テスト

興味が出てきたら

買って読んでね

Page 28: Nds#24 単体テスト

ご清聴ありがとうございました