開発者による現実的な自動化テスト

及びRubyのテストツールについて
Takaaki Kasai
2015.03.12
テストそのものの話
テストを自動化してますか?
なぜテストの自動化が必要なのか
• ここでいうテストとは開発者が自ら書くテストコードのこと
• リグレッションテストの労力と時間を大幅に削減できる
• 以前書いたコードを壊していないことが保証できる

(少なくともテストコードの仕様上は)
• 開発者自身の心の安寧が得られる ← 重要

(余計なことを考えず今眼の前にあるコードに集中できる)
• リファクタリングが躊躇なくできる

(壊したらどうしよう・・・なんて心配が不要になる)
そもそも「テストコードを書く」文化がない場合は?
• 「勝手にやりましょう」
• 人間、目の前で見せられるまで納得しない

→ いきなり「テストを書く時間をとらせてくれ」よりも

  勝手にやってしまって、効果を見せた方が話が早い
やっちまえ!
テストを書くのは品質の保証のため?
• 開発者自身が品質保証のためにやるのは大変
• あくまで

「リグレッションテストの手間と時間の削減」

「開発者の精神的安定と効率と集中力向上」

「リファクタリングによるコードの洗練」

のためにやるという考え方 ← 今回の話はこれ
• 完全なテストはいらない

→ 過去のコードを壊していないか確認できればいい
テストは過去のコードをチェックする
センサーみたいなもの
カバレッジ⃝⃝%なんて目指さない
• ぶっちゃけ、異常系なしで正常系だけでもいい



そもそも、新しいコードを追加して過去のコードを壊すとき
は、正常系が壊れることが多い。異常系の細かい部分が一箇
所だけ壊れることを確認するよりも、正常系だけの確認をし
たほうが圧倒的に効率がいい。
• テストを書く目的が品質保証ではなく「開発者自身のため」
であるからこその割り切り

(テストケースを細かく完璧に書く時間がない・・・・)
• ということで、カバレッジは気にしない。
単体テストより統合テスト
• テストを書く目的が過去のコードを壊していないかの確認な
ので、統合テストだけでも目的は果たせる
• もちろん単体テストはないよりあったほうがいい。書く時間
があれば。
• 時間が足りない時は、とりあえず先に実装を終わらせて後か
ら統合テストだけ書く
「時間は作るもの」
なんて言うけど限界ある
TDDじゃないの?
• テスト駆動は確かに「理想」

→ だけど、いちいちやってる時間あるか?
• 全てをテストファーストにするのはあきらめる

→ 正直、UIまで含めてテストファーストはキツイ

→ そこまで秀才じゃないッス
• 「雪だるま式」という考え方もある

→ 雪だるまは、最初に「核」になる部分を

  こねくり回して作って、それから転がして大きくする。

  同じように、最初はテストを書かずに「核」を作って、

  それからテスト駆動のサイクルを回す。
ツールの話
現在の自動テスト環境
• Cucumber
• Capybara
• Poltergeist
• PhantomJS
どういうこと?
Poltergeist
Poltergeist
Cucumber
Capybara
PhantomJS
処理の流れ
自然言語
→コード
への変換
Webアプリの
テスト向けDSL
PhantomJS用
ドライバ
ヘッドレス
ブラウザ
Cucumberって?
• テストコード記述ツールの進化

Test::Unit → RSpec → Cucumber
• Test::Unit

Rubyのソースコードそのもの
• RSpec

ソースコードに自然言語による説明がつけられる
• Cucumber

自然言語によるテスト仕様記述とソースコードを分離
サンプルアプリケーション
Hello! ボタンを押すと Hello, World!! と表示するだけ
test_rack_app.rb
config.ru
実行コマンド
$ rackup -o 0.0.0.0
Test::Unit
• Rubyのソースコードそのもの
• メソッド定義がテストの単位(先頭に test_ をつける)
• アサーション用のメソッドが用意されている(assert_*)
Test::Unit(コード例)
test_unit.rb
Test::Unit(実行例:成功時)
Test::Unit(実行例:失敗時)
• ソースコードに自然言語による説明がつけられる
• ブロックでテストの単位を表す
• より柔軟なアサーション: expect(*).to ∼
RSpec
RSpec(コード例)
rspec_spec.rb
RSpec(実行例:成功時)
RSpec(実行例:失敗時)
• 自然言語によるテスト仕様記述とソースコードを分離
• featureファイル: テスト仕様記述

→ gherkin(ガーキン)という言語、ほぼ自然言語で書ける
• step定義ファイル: ソースコード

→ featureをrubyのコードに翻訳
Cucumber
Cucumber(コード例 1/2)
features/hello.feature
Cucumber(コード例 2/2)
features/step_definitions/hello_steps.rb
features/support/env.rb cucumber.yml
Cucumber(実行例:成功時)
Cucumber(実行例:失敗時)
テストコードをもっと便利に
• Webアプリケーションでよく使う動作や確認

例えば・・・・

・フォーム内要素への入力

・HTMLの特定のタグ内に文字列があることを確認

・ボタンをクリック

→ 楽にテストを記述したい!
• そこで、Capybara

Webアプリのテスト向けDSLが っている

CSSセレクタやXPathが使える

HTMLの特定要素の属性やテキストの比較がラクにできる
Capybara+Cucumber(コード例)
features/step_definitions/hello_steps.rb
features/hello.feature features/support/env.rb
Capybara+Cucumber(実行例: 失敗時)
CSSセレクタで比較の範囲を絞ったので、エラーが分かりやすくなった
→ もちろん、自力でHTMLを解析して比較すれば同様のことは
  できるけど、しんどい。Capybaraを使えばラク。
ステップ定義に柔軟性をもたせる
step定義ファイル(抜粋)
→ CSSセレクタをfeatureファイル側に含めると、
  stepの汎用性がぐっと高まる
featureファイル(抜粋)
柔軟なステップ定義の例をもう少し
step定義ファイル(抜粋)
featureファイル(抜粋)
さらにJavascirptもテストしたい
• 統合テストであるからには、JSも含んだ動作を検証したい

→ JSのみの単体テストじゃなくて、ページ遷移なども

  含めて自動でテストしたい
• Seleniumって選択肢もあるけど、重い・扱いにくい
• そこで「ヘッドレスブラウザ」を使う

→ 最近よく使われるのが PhantomJS
「コマンド一発打って待ってれば
全部完了」が理想ですね
ヘッドレスブラウザ PhantomJS
• 中身はWebKit

正確には QtWebKit
• 公式サイトにあるビルド済みバイナリを使うのが吉

→ 自力でビルとすると30分以上かかる可能性大

→ 2ヶ月程前にメジャーバージョンアップ(2.0)したが、

  2.0のビルド済みバイナリは未提供(2015.03.12現在)

→ なので、バージョン1系列の過去のバイナリを使う

https://code.google.com/p/phantomjs/downloads/list
• たまにクラッシュします(笑)さすがにWebKitを完全に動かすのは簡単じゃないっぽい

落ちた部分だけ再度テストを実行しましょう。
PhantomJSを使うには
• Capybaraは、アプリケーションにアクセスしに行く部分のモ
ジュール(=ドライバ)を選択できる

※デフォルトではRack::Test
• CapybaraからPhantomJSを使うためには、

ドライバとしてPoltergeistを使う
features/support/env.rb
追記
追記
左記の設定で、
シナリオの直前に
@javascript
と記述(タグ)をつけた
シナリオはPoltergeistド
ライバ経由でPhantomJS
を使うようになる
AJAXをテストする
features/support/wait_for_ajax.rb
step定義ファイル(抜粋)
featureファイル(抜粋)
リモートサーバをテストする
features/support/env.rb(抜粋)
基本的にはこれだけ
• ステップ定義は変わらない
ステップに長いデータを渡す
step定義ファイル(抜粋)
featureファイル(抜粋)
→単に長い文字列を渡すためだけじゃなくて、
rubyのコードを書いてevalしたり、
JSONとして評価したり色々できる
この部分がステップの
最後の引数になる
データの初期化はどうする?
• env.rbに書く

テスト実行前のフックに登録する

データへのアクセスが意外と面倒だったりする
• アプリ内にテスト専用コントローラを用意する

アプリ内部からデータにアクセスするので色々ラク

テストコードを実行するマシンとテスト環境用データがある
マシンが分離している場合にも使える

もちろん本番環境からは除外するし、テスト環境限定で動く
ようにしておく

例:http://hostname/test/init-data にアクセスすると初期
化実行
Capybara+Cucumber+PhantomJS
実際の例&デモ
ありがとうございました

開発者による現実的な自動化テスト及びRubyのテストツールについて