codeceptionphalcon/incubatorphalconの単体テストをやってみました。

codeceptionではブラウザと実際に稼働しているアプリケーションを使ったAcceptanceTest、アプリケーション(各フレームワーク対応)をテスト実行時に内部で動かすFunctionalTest、従来型の単体テストを行うUnitTestがあります。今回はUnitTestでの単体テストの方法を書いていきます。

1. codeceptionのインストール

以下のコマンドを実行してインストールします。

2. phalcon/incubatorのインストール

composerでphalcon/incubatorをインストールします。composer.jsonはこんな感じで↓

あとは以下のコマンドを叩くだけ。

3. codeceptionの設定

まずはbootstrapでテンプレートを作ります。

codeceptionのテストですが、codecept runコマンドを実行したディレクトリのcodeception.ymlを読み取って、記載された設定に従ってテストを行うことになります。

codeception.ymlはこんな感じです。

paths.testsはテストコードが格納されているディレクトリで、ここに格納されているテストコードを対象にテストを実行することになります。

-eオプションを付けてテンプレートを作成すると、suite(テストのグループ)は何も作成されないので、以下のコマンドで単体テスト用のsuiteを作成します。codeceptionでテストを実行するためには必ずsuiteを作成する必要があります。

そうするとtestsディレクトリの中にunitディレクトリとunit.suite.ymlという設定ファイルが作成されます。全体的なディレクトリ構造はこんな感じになります。

codeceptionでは指定ディレクトリ内(今回はtests)の***.suite.ymlの設定ファイルを読み込んだ後、対象のディレクトリ内のテストが順次実行されることになります。また、boostrapファイル(codeception.ymlで_bootstrap.phpと定義しているファイル)が各テストの前に実行されます。

読み込まれるファイルとテスト実行の流れは以下のとおりです。

  1. ルートの(testsフォルダ)のbootstrapファイルの読み込み(bootstrapファイルの名前はcodeception.ymlで定義)
  2. 各スイート(ディレクトリ)のテストクラスの読み込み+インスタンス化(メソッド単位でインスタンス化されるっぽい)
  3. 各スイートのbootstrapファイルの読み込み
  4. 各テストコード実行(setup→コード実行→teardown)
  5. 2-4の繰り返し

ルートのbootstrapで各テストで必要な定数の定義やクラスのローディングをすることになります。
具体的には以下の様な感じになります。

Codeception\Util\Autoload::addNamespaceでは名前空間とディレクトリパスを指定することで対象ファイルのクラスを自動的に読み込んでくれます。
_supportフォルダ内に入れたものも自動的に読み込まれる仕様らしいので、そこに入れてもOK。

あとは以下のコマンドをアプリケーションのルートディレクトリで実行すればOK。

–debugオプションを付けるとcodecept_debug()関数での出力も表示されるのでローカルでテストするときは付けたほうが良いかも。

4. テストコードの作成

tests/unit/UTSampleTest.phpに以下のコードを書きます。

Phalcon\Test\FunctionalTestCaseはphalcon/incubatorのクラスになりますが、setUp内でparent::setup()をすると、FunctionalTestCaseの継承元のModelTestCaseで色々と不都合が起きてしまうため(※1)、parent::setUp()していません。その代わりに、parent::setUp()で行っている、DIの設定、アプリケーションの設定を明示的に行っています。

FunctionalTestCaseではプロパティの$applicationを使って内部でphalconを動かします。具体的には以下の様なコードになります。

(※1 $this->setDb();でデフォルト引数でmysqlを利用していたりするので、postgresql使ってたりするとエラーになる)

5. リクエストの書き方

コントローラ上で$this->request->getQuery()とか$this->request->getRawBody()とか、request変数経由で値を取得している場合は、request自体がDIコンテナを利用しているので、以下のようにしてテストすることが出来ます。

\Phalcon\Http\Requestのメソッド判定やURLパラメータ、ヘッダの取得は$_SERVER、$_GETを利用しているので、それらのスーパーグローバル変数を書き換えればOKです。getRawBodyに関してはphp://inputを出力しているので、php://inputに入力すれば良い気がしますが、何となく扱いづらそうな感じがしたので、今回はスタブでオーバーライドしてます。

6. その他

phalconはDIが色々便利なので、テストに使いたいコンポーネントはDI使うと良い気がします。例えばSendGridなんかを使ってメール送信をする場合、テストの都度メールが飛ぶのは面倒なので以下のようなスタブをDIコンテナに突っ込んでテストするとメールが飛ばなくなりますし、ちゃんとコールされたかどうかの検証も出来ます。

以下、その他ハマりポイント&TIPS。

codecept_debugでPhalconのモデルをデバッグしようとするとメモり使い果たしエラー発生(Allowed memory size of *** bytes exhausted)
→モデルの中が良い感じに循環参照になっているため、json_encodeするなりしてプロパティだけ取得して出力する必要があります。

Class not foundって言われるとき
→だいたいrequireできてないので、読み込まれるファイルの順番とオートローダの記載を要確認。

指定のディレクトリのテストのみ実行させたい

指定のクラスのテストのみ実行させたい

指定のメソッドだけ実行させたい

 

この投稿はPhalcon Advent Calendar 2015の 7日目の記事です。