Functionalテスト | Test

Functionalテストでは、Sabelアプリケーションのテストを行うことができます。
※Functionalテストを実行するには PHPUnit が必要です。

例として、下記のアクションをテストしてみましょう。
class Index_Controllers_Index extends Sabel_Controller_Page
{
  public function index()
  {
    $value = $this->request->fetchGetValue("value");
    
    if (is_number($value)) {
      $this->answer = 10 * $value;
    } else {
      $this->response->getStatus()->setCode(Sabel_Response::BAD_REQUEST);
    }
  }
}
ビュー(テンプレート)は次のようなものとします。
<p>
  10倍した結果は「<?php echo h($answer) ?>」です。
</p>
このアクションは入力値($_GET['value'])が整数ならば、それを10倍した答えを "answer" として出力、整数でなければ 400 Bad Request を返すものです。is_number()関数 についてはこちらを参照してください。

それでは、テストを作成します。Functionalテスト用のテストケースは tests/functional 以下に配置します。ここではテストケースの名前を Index とします。
tests/functional/Index.php

class Functional_Index extends Sabel_Test_Functional
{
  public function testIndexAction()
  {
    
  }
}
テストメソッドのメソッド名は必ず "test" で始まるようにします。

アプリケーションを実行するには、リクエストオブジェクトを生成し $this->request() メソッドをコールします。リクエストオブジェクトの setGetValue() や setPostValue() メソッドで GET, POST値 をセットすることができます。
class Functional_Index extends Sabel_Test_Functional
{
  public function testIndexAction()
  {
    $request = new Sabel_Request_Object("/index/index");
    
    $request->setGetValue("value", 30);
    $response = $this->request($request));
    $this->eq(300, $response->getResponse("answer"));  // "answer" が300と等しい
  }
}
また、GET値をセットする必要がない場合にリクエストオブジェクトの生成を省略できる httpGet()メソッド もあります。下記の2つのコードは同じ働きをします。
$request = new Sabel_Request_Object("/index/index");
$response = $this->request($request);
$response = $this->httpGet("/index/index");
テストを実行すると、結果が300であり、テストが正常であることが分かります。
※テスト実行(タスク)に関してはこちらを参照してください。
$ sakle Functional Index
PHPUnit 3.4.3 by Sebastian Bergmann.

.

Time: 0 seconds

OK (1 test, 1 assertion)
もちろん、マイナスの値でリクエストしても結果は正常となるでしょう。
class Functional_Index extends Sabel_Test_Functional
{
  public function testIndexAction()
  {
    $request = new Sabel_Request_Object("/index/index");
    
    ...
    
    $request->setGetValue("value", -10);
    $response = $this->request($request));
    $this->eq(-100, $response->getResponse("answer"));
  }
}
$ sakle Functional Index
PHPUnit 3.4.3 by Sebastian Bergmann.

.

Time: 0 seconds

OK (1 test, 2 assertion)
アクションの実装では整数でない値が渡された場合 400 Bad Request を返すようになっています。これは、レスポンス(ステータス)オブジェクトのステータスを見ることで検証が可能です。
class Functional_Index extends Sabel_Test_Functional
{
  public function testIndexAction()
  {
    ...
    
    $request->setGetValue("value", "+10");
    $response = $this->request($request));
    $this->eq(Sabel_Response::BAD_REQUEST, $response->getStatus()->getCode());
  }
}
$ sakle Functional Index
PHPUnit 3.4.3 by Sebastian Bergmann.

.

Time: 0 seconds

OK (1 test, 3 assertion)
仕様上 4xx系 のステータスコードになっていれば良いとするなら、下記のようなチェックでもいいかもしれません。
class Functional_Index extends Sabel_Test_Functional
{
  public function testIndexAction()
  {
    ...
    
    $request->setGetValue("value", "+10");
    $response = $this->request($request));
    $this->assertTrue($response->getStatus()->isClientError());
  }
}

Functionalテスト時のセッションの使用

アプリケーションがセッションを使用している場合、テストケースでも同様の振る舞いを実現しなければなりません。それを実現するために使用するのが、疑似セッションオブジェクトの Sabel_Session_InMemory クラスです。

では、例として、下記のコントローラを使用します。
class Index_Controllers_Index extends Sabel_Controller_Page
{
  public function login()
  {
    $this->session->write("auth", true);
  }
  
  public function privarea()
  {
    if ($this->session->read("auth")) {
      $this->response->getStatus()->setCode(Sabel_Response::FORBIDDEN);
    }
  }
  
  public function logout()
  {
    $this->session->delete("auth");
  }
}
loginアクションでセッションに "auth" フラグが立たない限り、privareaアクションは 403 Forbidden を返すというものです。これはもちろん、Webブラウザなどのクライアントからでも期待した通りの振る舞いをします。
テストでセッションを扱うには、セッションが関連する一連のリクエストにおいて同じセッションオブジェクトを渡してください。

まず、privareaアクションから 403 Forbidden が返されることをテストしましょう。
class Functional_Index extends Sabel_Test_Functional
{
  public function testAuthActions()
  {
    $session = Sabel_Session_InMemory::create();
    
    $response = $this->httpGet("/index/privarea");
    $this->eq(Sabel_Response::FORBIDDEN, $response->getStatus()->getCode());
  }
}
$ sakle Functional Index
PHPUnit 3.4.3 by Sebastian Bergmann.

.

Time: 0 seconds

OK (1 test, 1 assertion)
次にloginアクションを実行し、その後でprivareaアクションを実行してみます。"auth" フラグが立つのでprivareaアクションからは 200 OK が返されるはずです。
class Functional_Index extends Sabel_Test_Functional
{
  public function testAuthActions()
  {
    $session = Sabel_Session_InMemory::create();
    
    ...
    
    $this->httpGet("/index/login");
    
    $response = $this->httpGet("/index/privarea");
    $this->eq(Sabel_Response::OK, $response->getStatus()->getCode());
  }
}
$ sakle Functional Index
PHPUnit 3.4.3 by Sebastian Bergmann.

.

Time: 0 seconds

OK (1 test, 2 assertion)
最後にlogoutアクションを実行し、その後でもう一度privareaアクションを実行してみましょう。"auth" フラグが消え、privareaアクションからは再び 403 Forbidden が返されるはずです。
class Functional_Index extends Sabel_Test_Functional
{
  public function testAuthActions()
  {
    ...
    
    $this->httpGet("/index/logout");
    
    $response = $this->httpGet("/index/privarea");
    $this->eq(Sabel_Response::FORBIDDEN, $response->getStatus()->getCode());
  }
}
$ sakle Functional Index
PHPUnit 3.4.3 by Sebastian Bergmann.

.

Time: 0 seconds

OK (1 test, 3 assertion)

Functionalテスト時のリダイレクト

Functionalテストはリダイレクトにも対応し、リクエスト毎のレスポンスを得ることができます。リダイレクトを有効にするためには request()メソッド や httpGet()メソッドの 第3引数に $maxRedirects の値を渡します。

では、リダイレクトをテストでどう扱えるのか、下記のコントローラで試してみます。
class Index_Controllers_Index extends Sabel_Controller_Page
{
  public function index()
  {
    $this->v = 10;
    $this->redirect->to("a: foo");
  }
  
  public function foo()
  {
    $this->v = 20;
    $this->redirect->to("a: bar");
  }
  
  public function bar()
  {
    $this->v = 30;
    $this->redirect->to("a: baz");
  }
  
  public function baz()
  {
    $this->v = 40;
  }
}
Webブラウザでindexアクションを実行すれば、リダイレクトを重ね一気にbazアクションまで到達します。

テストでも、次のようにすることで同じ振る舞いを実現できます。
class Functional_Index extends Sabel_Test_Functional
{
  public function testRedirects()
  {
    $responses = $this->httpGet("/index/index", null, 5);
  }
}
上記では $maxRedirects として5を指定しています。これは最初のアクションも含めると、最大6回のリクエストまで実行することを示します。

通常の request() や httpGet()メソッド の戻り値はレスポンスオブジェクトでしたが、$maxRedirects を指定した場合は、レスポンスオブジェクトの配列が返されます。リクエストの順に、各リクエストのレスポンスオブジェクトが配列に格納されています。

レスポンスオブジェクトからは下記のように、リダイレクトされたかどうか、また、リダイレクト先のURIを取得することが可能です。
$response->isRedirected();
$response->getLocation();
これらのことを踏まえ、このコントローラのテストは次のようなコードでテストが可能です。
class Functional_Index extends Sabel_Test_Functional
{
  public function testRedirects()
  {
    $responses = $this->httpGet("/index/index", null, 5);
    
    $this->eq(4, count($responses));
    
    $this->eq(10, $responses[0]->getResponse("v"));
    $this->assertTrue($responses[0]->isRedirected());
    $this->eq("/index/foo", $responses[0]->getLocation());
    
    $this->eq(20, $responses[1]->getResponse("v"));
    $this->assertTrue($responses[1]->isRedirected());
    $this->eq("/index/bar", $responses[1]->getLocation());
    
    $this->eq(30, $responses[2]->getResponse("v"));
    $this->assertTrue($responses[2]->isRedirected());
    $this->eq("/index/baz", $responses[2]->getLocation());
    
    $this->eq(40, $responses[3]->getResponse("v"));
    $this->assertFalse($responses[3]->isRedirected());
  }
}
$ sakle Functional Index
PHPUnit 3.4.3 by Sebastian Bergmann.

.

Time: 0 seconds

OK (1 test, 12 assertion)