出力に関するテスト
Webアプリケーションに関するテストにはさまざまなものが考えられると思いますが、最も基本となるのは表示される内容に関するテストになると思います。そこで、まず表示される内容(出力)に関するテストを記述するためのメソッドについて見ていきましょう。
出力をテストするメソッド
出力に関するテストは文字通り「どんな場所」に「どんな内容」が出力されているかをテストするメソッドです。このうち「どんな場所」を指定する方法にはCSSセレクタとXPathの2種類が準備されており、また「どんな内容」を指定する方法には「指定なし」「一致」「正規表現」の3種類の方法が準備されています。
このうち、CSSセレクタを利用して場所を指定するテストの記述に利用できるメソッドは、次のとおりです:
| メソッド名 | 引数 | 説明 |
| assertQuery | $path | 出力に$pathの要素が存在するかどうかを検証 |
| assertQueryContentContains | $path, $match | 出力の$pathで示された要素の内容が$matchにマッチするかどうかを検証 |
| assertQueryContentRegex | $path, $pattern | 出力の$pathで示された要素の内容が正規表現$patternにマッチするかどうかを検証 |
| assertQueryCount | $path, $count | 出力に$pathで示された要素が$count個あるかどうかを検証 |
| assertQueryCountMin | $path, $count | 出力の$pathの要素数が$count個以上であるかどうかを検証 |
| assertQueryCountMax | $path, $count | 出力の$pathの要素数が$count個以下であるかどうかを検証 |
これらの利用方法を、サンプルを利用して説明します。なお、CSSセレクタの詳細については後から説明します。
class IndexControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
{
...
public function testQuery() {
$this->request->setMethod('POST')
->setPost(array(
'username' => 'codezine',
'password' => 'php'
));
$this->dispatch('index/login');
$this->assertQuery('p');// (1)
$this->assertQueryContentContains('p', 'correct');// (2)
$this->assertQueryContentRegex('p', '{Paragraph.}');// (3)
$this->assertQueryCount('p',3);// (4)
$this->assertQueryCountMin('p', 1);// (5)
$this->assertQueryCountMax('p', 5);// (6)
}
}
(1)はCSSセレクタで「p」にマッチする要素が、ディスパッチされたindex/loginの中に存在しているか検証しています。(2)は(1)のようにp要素が存在し、さらにのp要素の内側にcorrectという文字列があるかという検証です。(3)は(2)と同じような関数ですが、p要素の内側の文字列を正規表現で指定しています。(4)~(6)はp要素の数に関するテストで、(4)は「p要素が3つである」、(5)は「p要素が1つ以上である」、(6)は「p要素が5つ以下である」をそれぞれ検証しています。
前回のままでは(3)で指定した「Paragraph.」(「.」は任意の1文字)がないためテストに失敗してしまい、また(4)も要素数があっていません。これらのテストに通るために「login.phtml」を以下のように書き換えます。
<html> <head> <title>Codezine Zend_Test</title> </head> <body> <h1>結果</h1> <p>Paragraph1</p> <!-- 追加 --> <p>Paragraph2</p> <!-- 追加 --> <p><?php echo $this->result; ?></p> </body> </html>
「<p>Paragraph1</p>」を追加したことで(3)の条件にマッチし、さらにもう1段落追加することでp要素の数が3つになるためすべてのテストが通ります。
CSSセレクタ
上記で使用したCSSセレクタとは、パターンマッチの規則に従って、HTML、XMLといったツリー構造で要素を指定する方法です。単純に要素名だけで指定することもできますし、特定の要素の中にある要素といったふうに細かく指定することも可能です。サンプルコードで正しいユーザ名、パスワードを入力したときの結果を例に見てみましょう。ログイン後のページのソースコードを見てください。
<html>
<head>
<title>Codezine Zend_Test</title>
</head>
<body>
<h1>結果</h1>
<p>Paragraph1</p> <!-- (1) -->
<p>Paragraph2</p> <!-- (2) -->
<p>correct</p> <!-- (3) -->
</body>
</html>
例えば「p」と指定すればどこにあるp要素にもマッチするため(1)~(3)のすべてのp要素にマッチします。もっと細かく、例えば(1)のp要素だけにマッチさせることも可能です。「h1 + p」と指定すればh1要素の直後のp要素にマッチするのでh1要素の直後にある(1)だけにマッチします。CSSセレクタにはこの他にもさまざまな要素の指定パターンが存在します。
| パターン | 意味 |
| * | すべての要素 |
| E | すべてのE要素 |
| E F | E要素の内部のF要素(子孫関係) |
| E > F | E要素直下のF要素(親子関係) |
| E + F | E要素の直後に隣接しているF要素(兄弟関係) |
| E[attribute] | attribute属性を持つE要素 |
CSSセレクタの指定パターンは他にもあるので、詳細については「5.1 パタン一致化」などを参照してください。
XPath、Notを利用したアサーション
出力に関するテストでは、先程紹介したメソッドが基本となりますが、これ以外にも少しバリエーションがあります。まず、1つ目は「場所」の指定にCSSセレクタではなく、XPathを利用するメソッドです。さらにもう一つ、テストの否定版があります。つまり、先程紹介したメソッド1つあたり4つのバリエーションがあるわけです。
| メソッド名 | 引数 | 説明 |
| assertQuery | $path | CSSセレクタを利用、$pathの要素が存在するかどうかを検証 |
| assertXpath | $path | assertQueryと同等であり$pathをXPathで指定 |
| assertNotQuery | $path | aseertQueryの否定であり$pathの要素が存在しないかどうかを検証 |
| assertNotXpath | $path | aseertQueryの否定であり$pathをXPathで指定し、$pathの要素が存在しないかどうかを検証 |
XPathはCSSセレクタのように要素を指定する方法です。例えば「p」と要素名だけの指定ではCSSセレクタと違いがありませんが、CSSセレクタでの「h1 + p」は、Xpathで表すと、「 h1/following-sibling::*[1][self::p] 」となります。
XPathの指定パターンについての詳細は、公式のドキュメント(英語)や、その翻訳などを参照してください。
また、各メソッドには基本的に、アサートメソッドに逆の仮定を行うNotメソッドが用意されています(ただし、要素数に関するテストである「assertQueryCountMin」「assertQueryCountMax」「assertXpathCountMin」「assertXpathCountMax」を除く)。
さて、これらの使い方は上で説明した出力テストメソッドと同じです。実際に使用してみましょう。
class IndexControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
{
...
public function testQuery2() {
$this->request->setMethod('POST')
->setPost(array(
'username' => 'codezine',
'password' => 'php'
));
$this->dispatch('index/login');
$this->assertXpath('//p');// (1)
$this->assertNotQuery('h2');// (2)
}
}
(1)の動作はassertQuery('p')と同じですがパスをXPathで指定しています。もちろんこれは「p」でもいいのですがCSSセレクタでなくXpathで指定していることを強調するために「//p」を使用しています。
Notメソッドは逆の仮定を行います。(2)は<h2>が存在しないかどうかの検証で存在していないので通ります。
