「バリデーション」APIと「単体テスト」APIの類似性,およびそのスタイルが時代と共に洗練される過程の概観
「テスト」と「バリデーション」は,互いに酷似している。
- 単体テスト:開発者が,システムの入出力の振る舞いを検証すること。
- バリデーション:システムが,ユーザの入力値を検証すること。
ユニットテストとバリデーションは本質的に同類の処理であり,
その実装のために提供されるAPIも必然的に酷似するのである。*1
これら2つのコードは,工程やレイヤはまるっきり異なるが,まるで
- システムの同じ挙動を,2つの異なった角度・見地から見ている
かのような,そっくりの類似性を持つことになる。
両者の違いは,下記のように要約される。
- 前者はシステム仕様を検証するためのテストケースであり,後者はシステム仕様そのものである。
- 検証操作を行なう主体と対象も異なる。(上図を参照)
本稿では,各種テストAPIや,各種バリデーションAPIのイディオムをさらっと概観する。
両者のAPIが確かに「共通の視点」を持っている事に気づくはずだ。
この「共通の視点」を認識すると,システム設計のアイデアの幅が広がる。
例えば,テスティングフレームワークのMatcher APIを調べれば,
次世代のバリデーション・プラグインを開発するために役立つのではないか。
また同時に,それらのAPIのコーディング・スタイルが
時代と共に洗練され,よりメタで自然な記述になってきた様子も確認できると思う。
- (1)レガシーテストツール:assert系API
- (2)モダンなテストツール:DSL,自然言語,設計のためのテストAPI
- (3)バリデーションの記述スキーム
(1)レガシーテストツール:assert系API
assert系のメソッドを羅列してテストを記述する。
共通イディオム:
- assertEquals
- assertTrue
- assertNotNull
JUnit (Java単体テスト)
(2006) JUnit−2.基本操作
http://www.javaroad.jp/opensource/js_...
- 3.x系
TestNG (Java単体〜受け入れテスト)
(2008年)JUnitより簡単なオープンソースの「TestNG」とは?
http://www.atmarkit.co.jp/fjava/rensa...
- JUnit3.xと4.xの間に誕生
- アノテーションを駆使
(2006年)コード品質を追求する: JUnit 4 対 TestNG / 大規模なテストでは TestNG のほうが優れたフレームワークになる理由
http://www.ibm.com/developerworks/jp/...
- JUnitの主眼は単一のオブジェクトに対する検証。TestNGはもっと規模を大きくできる
JSUnit (JavaScript,ブラウザテスト)
(2001〜)Test Examples
http://www.jsunit.net/examples/index....
- jQuery作者のJohn Resigに言わせると「Code feels very 2001」(後述)
※拙作の「SimpleJSUnit」というのもある。(JavaScript + WSH/JScript)
JavaScriptの単体テストフレームワーク "simpleJsUnit" で,テスト駆動開発をしよう
http://language-and-engineering.hatenablog.jp/entry/20090413/p1
- a()
Selenium (Selenium1.x, もしくはSelenese。ブラウザ上の総合テスト)
selenium 主なコマンド一覧
http://language-and-engineering.hatenablog.jp/entry/20081016/1224123118
- assert系のメソッドが,WebページのDOM操作に特化して機能豊富になった。
イディオム:
- verifyValue
- verifyVisible
- verifyElementPresent
- waitForElementPresent
- verifyTable
Selenium 1 (Selenium RC)
http://seleniumhq.org/docs/05_seleniu...
(2)モダンなテストツール:DSL,自然言語,設計のためのテストAPI
テストケース自体が,テスト対象オブジェクトの仕様書となる。
自然言語への近さや,DSLとしての自然さを意識。
テスト駆動開発を一歩進めて,振る舞い(Behavior)駆動開発になる。
RSpec (Ruby単体テスト)
(2010年)Start! Ruby RSpecの構文
http://www39.atwiki.jp/startruby/page...
- クロージャ(ブロック)とメタプログラミングを駆使
- 「should」で検査できるような多数のMatcher群の一覧表
イディオム:
- describe
- should be_true
- should be_empty
- have(n).items
- respond_to
- satisfy
※JavaScript版の「JSSpec」もある。
QUnit (JavaScript & jQuery,ブラウザテスト)
John Resig(jQueryの作者):Understanding JavaScript Testingのスライド
http://ejohn.org/blog/talks-at-the-20...
jQuery Documentation - QUnit
http://docs.jquery.com/QUnit
イディオム:
- ok
- asyncTest
- equal
- notEqual
WebDriver (旧Selenium)
(2011/08) WebDriverを統合したSelenium 2を使ってみる
http://d.hatena.ne.jp/hutyao/20110822...
(2011/10) Selenium 2.0 の日本語入門記事があまり検索でヒットしなかったので僕が書いておく
http://bearmini.net/blog/View.aspx?bi...
WebDriverのJavaDoc
http://selenium.googlecode.com/svn/tr...
Selenium 2.0 and WebDriver
http://seleniumhq.org/docs/03_webdriv...
イディオム:
- driver.findElement(By.id(〜)) / By.linkText(), By.tagName(), By.xpath()
- (new WebDriverWait(driver, 10)).until(new ExpectedCondition〜)
- assertThat(driver.getTitle(), is("selenium - Google 検索"));
- 「述語クラス」
Cucumber (Cuke, Ruby受け入れテスト)
(2009)「プロジェクト特化言語 という夢を見たんだ」というスライド
http://d.hatena.ne.jp/moro/20090707/1...
- お客様に扱って頂けるような仕様書を自然言語で記述し,それがそのままテストコードとなる(実行可能ドキュメントの発想)。
イディオム:
- フィーチャ
- シナリオ
- もし
- ならば〜であること
(3)バリデーションの記述スキーム
アプリケーション・フレームワークには,バリデーション・ルールの記述スキームが存在する。
こういったバリデーション用サブシステムが持つAPIには,
テスティング・フレームワークと共通する部分もかなり多い。
Struts(Java, MVCフレームワーク)
Struts−17.Validator
http://www.javaroad.jp/opensource/js_...
- Validatorで確認できるルールの一覧表とサンプル。validation.xmlに記述
イディオム:
- required
- validwhen
- minlength
- date
- url
Ruby on Rails(Ruby, MVCフレームワーク)
validateのバリエーション
http://d.hatena.ne.jp/zariganitosh/20...
- Modelクラスに記述する(2.x系まで推奨の)validates系メソッド
イディオム:
- validates_length_of
- validates_numericality_of
- validates_presence_of
- validates_size_of
validates :rails_3, :awesome => true
http://lindsaar.net/2010/1/31/validat...
- As you would know from Yehuda’s post on Active Model abstraction, in Rails 3.0, Active Record now mixes in many aspects of Active Model, including the validates modules.
- Rails version 3 offers you some cool, nay, awesome alternatives:
3.x系からの記述方法:
validates :name, :presence => true, :length => {:minimum => 1, :maximum => 254} validates :email, :presence => true, :length => {:minimum => 3, :maximum => 254}, :uniqueness => true, :format => {:with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i}
Symfony(PHP, MVCフレームワーク)
symfony Forms in Action / 第2章 - フォームのバリデーション
http://www.symfony-project.org/forms/...
- PHPなので,イディオムを洗練させることはまず難しい。そのため,処理フローのみに注目
- 送信されたform単位でvalidate()を実行し,内部では1項目ごとに1カラムタイプ(メールアドレスとか)に対応させるためのValidatorを割り当て
- 同じPHPで言うと,smarty/XOOPS等の処理フローも似たようなもの
Hibernate Validator(Java, ORマッピングツールのプラグイン)
Hibernate Validatorを使う
http://www.grandnature.net/blog/archi...
- エンティティクラスにアノテーションで制約を記述する。 検証ルールに反する値をセットしてsave()すると例外を投げる
(2007)Java API、使ってますか? / 8 アノテーションでバリデーション - JSR 303: Bean Validator
http://news.mynavi.jp/column/jsr/008/...
- JSR 303はJava Beanオブジェクトのためのバリデーション機構やメタデータモデルを実現する目的で発足。このAPIを使用することで、Java Beanのプロパティが取り得る値の範囲や条件を設定し、実行時に統一的な方法でそれを検証できる
- Commons Validator : 美しくない。
イディオム:
- @NotNull
- @Range(min = 0, max = 100)
- @Past(過去日付)
JSR 303 Bean Validator(Java,エンタープライズ向けの言語仕様の一部)
JSR 303 Bean Validationで遊んでみるよ!
http://d.hatena.ne.jp/yamkazu/2011020...
- データストアに問い合わせしないといけないValidationって画面の入力だけでチェックできるのとどう管理しようかなとか、色々と悩むこともしばしば
- DDD的に色々考えると、基本的には検証ルールはドメインのそのEntity自身が知っているべき
記述形式より抜粋:@Max.List({ @Max(value = 10, groups = NormalUser.class), @Max(value = 100, groups = PremiumUser.class) })イディオム:
- @AssertTrue
- @DecimalMax
- @Future
↑このあたりまで読むと,ユニットテストとバリデーションは本質的に同類の処理であり,提供されるAPIも必然的に酷似するという事情がいよいよ明白になってくるだろう。
下記の記事も参照。
システムの各レイヤにおけるバリデーション・ロジックの名称を分類して,キャッチーな呼び名を付けている。
あまり知られていない,Webアプリ開発時の10の略語 (例文つき)
http://language-and-engineering.hatenablog.jp/entry/20101102/p1
- クラバリ
- サーバリ
- コンバリ(J2EEパターンで言うと「フォームベースのバリデーション」)
- モデバリ(同「抽象型ベースのバリデーション」)
- デーバリ
結論
本エントリの結論として,次のような事が言える。
- 単体テストと,バリデーションロジックは,互いに酷似している。したがって・・・
- テスティング・フレームワークを設計する際には,バリデーション・ロジックの設計を参考にする事ができる。
- バリデーション・ロジックを設計する際には,テスティング・フレームワークを参考にする事ができる。
追記
このエントリで記述した通りのことを,私は実際に行なった。
すなわち,自分が作ったアプリケーション・フレームワークのバリデーションAPIに,単体テストAPIの発想を取り入れたのである。
Androidアプリ開発用のMVCフレームワーク 「Android-MVC」 ver0.2をリリース
http://language-and-engineering.hatenablog.jp/entry/20120323/p1
- 「■バリデーション・ロジックが,きわめて簡潔に記述できるようになった。 」という項目を参照。
- assert系のAPIで,画面の入力内容を効率的にバリデーション可能。
そして,そのためのアイデアは,実現よりも1カ月以上前に公開済みであった。誰も気づくまい。
AndroidアプリにStrutsのようなコントローラを導入し,画面制御させるサンプルコード (の試作品。バリデーションやビジネスロジックの骨組み)
http://language-and-engineering.hatenablog.jp/entry/20120213/p1
- バリデータをもっと楽に記述できるように,バリデーションロジックを豊富に取りそろえること
- まるでJUnitやRSpecやQUnitでテストコードを書くかのように,バリデーションロジックを記述できたら,すごくナイスで面白いと思わないか?assertEqual() とか,いろいろ
- バインディング機構とか導入すれば,バリデーションの概念が変わるはずだ
*1:ソフトウェア工学では,テスト工程の観点を描写するために「validation/verification」という用語が使われる。