【fuelphp】phpunitでのテスト自動化入門〜使い方メモ
はじめに
ずっとやろうやろうと思ってて放置していたテストです
残念なことに自分が携わってきた案件ではこれが使われていた案件はありませんでした。
・自分は心配性な方なので割とデバッグは入念にやるためか
・たまたま案件に恵まれていただけなのか
はわかりませんがテスト不足で痛い目にあった記憶があまりありませんでした(たぶん)
けど
・お金が関わってる処理とか
・ロジックがやたら複雑になってしまっているとか
を改修するとデグレードしてないかと心配はやはり耐えません
どこかでテストコードを書く事でこの心配が減らせるみたいなことを目にしてなるほどなぁと思ったのを覚えてます
導入
今回は何故か初めからphpunitはインストールしてありましたがなければインストールする
phpunitインストール
> wget https://phar.phpunit.de/phpunit.phar > chmod +x phpunit.phar > sudo mv phpunit.phar /usr/local/bin/phpunit > phpunit --version PHPUnit 4.8.23 by Sebastian Bergmann and contributors.
composer.jsonにphpunitを追加
versionはphpunitと合わせる
{ ・・・ "require-dev": { "phpunit/phpunit": "4.8.*", ・・・ } ・・・ }
composer update
php /project_path/composer.phar update
テスト対象
簡単ですが、以下のようなコントローラをテストしてみます
・EmailとパスワードにてDBを検索して情報があればセッションに保存するコントローラ
・ログインしている場合のみ見れるページのコントローラ
fuel/app/controller/member.php
class ValidationException extends Exception {} class Controller_Member extends Controller { // ログイン処理 public function action_login() { try { $email = Input::post("email"); $password = Input::post("password"); // 入力チェック if (!$email || !$password) { throw new ValidationException; } // Emailとパスワードで会員情報検索 ※例なので簡易版なので暗号化してない等々はお見逃しで $member = Model_Db_Member::getByEmailPassword($email, $password); if (empty($member)) { throw new ValidationException; } // ログインに成功したらセッションに保存 Session::set("member", $member); // 会員ページへリダイレクト $this->redirect("/member/index"); } catch(ValidationException $e) { $view = View::forge('member/login/form); $view->set("error", "Emailまたはパスワードが正しくありません"); return $view; } } // 会員専用ページ public function action_index() { // セッションから会員情報取得 $member = Session::get("member"); // 未ログインの場合はログインページへリダイレクト if (empty($member)) { $this->redirect("/member/login/form"); } // テンプレート $view = View::forge('member/index'); $view->set("member", $member); return $view; } // リダイレクト private function redirect($url) { if (isset($_SERVER["FUEL_ENV"]) && $_SERVER["FUEL_ENV"] == "test") { // testではリダイレクトしない } else { Response::redirect($url); } } }
テストコードの作成
@group・・・後でテストを実行するときにグループ単位で実行したいときに使う
@test・・・これがないとテストのメソッドにtestXXXの形でtestが必要になるのでつけておく
fuel/app/tests/controller/member.php
<?php /** * @group App */ class Test_Member extends TestCase { /** * @test */ public function ログイン成功のテスト() { // ログイン成功する(はずの)パラメータをセット $_POST["email"] = "xxx@xxx.com"; $_POST["password"] = "xxxxxxxx"; Session::destroy(); $response = Request::forge("member/login") ->set_method("POST") ->execute() ->response(); // 処理後にセッションに保存されていればログイン成功なのでテスト成功と見なす $member = Session::get("member"); $this->assertTrue(!empty($member)); } /** * @test */ public function ログイン失敗のテスト() { // ログイン失敗する(はずの)パラメータをセット $_POST["email"] = "xxxx@xxx.com"; $_POST["password"] = ""; Session::destroy(); $response = Request::forge("member/logindo") ->set_method("POST") ->execute() ->response(); // 処理後にセッションに保存されていなければログイン失敗なのでテスト成功と見なす $member = Session::get("member"); $this->assertTrue(empty($member)); } /** * @test */ public function ログインページ閲覧可のテスト() { Session::destroy(); // ログイン成功する(はずの)パラメータをセット $_POST["email"] = "xxx@xxx.com"; $_POST["password"] = "xxxxxxxx"; // ログイン処理を行う $response = Request::forge("member/login") ->set_method("POST") ->execute() ->response(); // 会員専用ページへアクセス(見れるはず) $response = Request::forge("member/index") ->execute() ->response(); // 処理後に変数に会員情報がセットされていればテスト成功と見なす $this->assertTrue(!empty($response->body->member)); } /** * @test */ public function ログインページ閲覧不可() { Session::destroy(); // ログイン失敗する(はずの)パラメータをセット $_POST["email"] = "xxx@xxx.com"; $_POST["password"] = ""; // ログイン処理を行う $response = Request::forge("member/login") ->set_method("POST") ->execute() ->response(); // 会員専用ページへアクセス(見れないはず) $response = Request::forge("member/index") ->execute() ->response(); // 処理後に変数に会員情報がセットされていなければテスト成功と見なす $this->assertTrue(empty($response->body->member)); } }
テスト実行
> php /project_path/oil test --group=App php oil test --group=App Tests Running...This may take a few moments. PHPUnit 4.8.27 by Sebastian Bergmann and contributors. ........ Time: 2.17 seconds, Memory: 21.00MB OK (4 tests, 7 assertions)
成功!!
おわりに
まだ実案件で導入したわけではなくて触りだけなので、実際にやりはじめると色々問題も出てきそうだけどとりあえず触りの段階としてはokとします。
本当はviewとかもやらないとダメなのかな・・・
自分なりにまとめると
1. モデルとコントローラは効果がありそうなのでなるべく導入する
2. 構文エラーや存在しないクラスやメソッド・関数を使用している箇所が発見できるので、if文とかは網羅する
3. viewはやり方まだよくわかってないのとそこまで致命的にならないでしょとういことで今まで通り手動かつ目でテストでいいのかな。。。
テストパターンの質はもちろん大切だとは思うけど、2.だけでもけっこう意味ある気がした
おまけ(一瞬詰まったところ)
phpunitとcomposer.jsonに記述するphpunitのバージョンが一致しなくてエラーとなった
php oil test --group=App Tests Running...This may take a few moments. PHPUnit 3.7.38 by Sebastian Bergmann. Configuration read from /var/www/html/example.com/fuel/app/phpunit.xml PHP Fatal error: Class PHPUnit_Util_DeprecatedFeature_Logger contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (PHPUnit_Framework_TestListener::addRiskyTest) in /var/www/html/example.com/fuel/vendor/phpunit/phpunit/PHPUnit/Util/DeprecatedFeature/Logger.php on line 201 PHP Stack trace: PHP 1. {main}() /usr/bin/phpunit:0 PHP 2. PHPUnit_TextUI_Command::main() /usr/bin/phpunit:44 PHP 3. PHPUnit_TextUI_Command->run() /usr/share/php/PHPUnit/TextUI/Command.php:101 PHP 4. PHPUnit_TextUI_TestRunner->doRun() /usr/share/php/PHPUnit/TextUI/Command.php:150 PHP 5. spl_autoload_call() /usr/share/php/PHPUnit/TextUI/Command.php:235 PHP 6. Composer\Autoload\ClassLoader->loadClass() /usr/share/php/PHPUnit/TextUI/Command.php:235 PHP 7. Composer\Autoload\includeFile() /var/www/html/example.com/fuel/vendor/composer/ClassLoader.php:301 PHP 8. include() /var/www/html/example.com/fuel/vendor/composer/ClassLoader.php:412 Fatal error: Class PHPUnit_Util_DeprecatedFeature_Logger contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (PHPUnit_Framework_TestListener::addRiskyTest) in /var/www/html/example.com/fuel/vendor/phpunit/phpunit/PHPUnit/Util/DeprecatedFeature/Logger.php on line 201 Call Stack: 0.0003 230368 1. {main}() /usr/bin/phpunit:0 0.2099 617056 2. PHPUnit_TextUI_Command::main() /usr/bin/phpunit:44 0.2099 617680 3. PHPUnit_TextUI_Command->run() /usr/share/php/PHPUnit/TextUI/Command.php:101 0.9668 15829872 4. PHPUnit_TextUI_TestRunner->doRun() /usr/share/php/PHPUnit/TextUI/Command.php:150 0.9875 15979328 5. spl_autoload_call() /usr/share/php/PHPUnit/TextUI/Command.php:235 0.9876 15979384 6. Composer\Autoload\ClassLoader->loadClass() /usr/share/php/PHPUnit/TextUI/Command.php:235 0.9876 15979384 7. Composer\Autoload\includeFile() /var/www/html/example.com/fuel/vendor/composer/ClassLoader.php:301 0.9879 16000928 8. include('/var/www/html/example.com/fuel/vendor/phpunit/phpunit/PHPUnit/Util/DeprecatedFeature/Logger.php') /var/www/html/example.com/fuel/vendor/composer/ClassLoader.php:412
以上です