ci-phpunit-testのモンキーパッチ機能でexitを例外に変換したときのデバッグ方法

例えば、テスト中にちょっとデバッグしたいときに、以下のように変数をvar_dump()して確認したいとします。

--- a/application/controllers/api/Example.php
+++ b/application/controllers/api/Example.php
@@ -40,7 +40,7 @@ class Example extends REST_Controller {
         ];

         $id = $this->get('id');
-
+var_dump($id); exit;
         // If the id parameter doesn't exist return all the users

         if ($id === NULL)

通常はexitでphpunitが終了し、以下のようになり楽に確認できます。

$ ../../vendor/bin/phpunit controllers/Uri_test.php 
PHPUnit 4.7.7 by Sebastian Bergmann and contributors.

......string(1) "1"

ところが、モンキーパッチでexitを例外に変換してしまっていると、

$ ../../vendor/bin/phpunit controllers/Uri_test.php 
PHPUnit 4.7.7 by Sebastian Bergmann and contributors.

......EE

Time: 7.93 seconds, Memory: 17.75Mb

There were 2 errors:

1) Uri_test::test_api_example_users_uri
CIPHPUnitTestExitException: exit() called in Example::users_get()

/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/application/controllers/api/Example.php:43
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/application/libraries/REST_Controller.php:654
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php:312
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php:256
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php:133
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestCase.php:106
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/application/tests/controllers/Uri_test.php:43
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/phpunit:36

2) Uri_test::test_api_example_users_ruri
CIPHPUnitTestExitException: exit() called in Example::users_get()

/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/application/controllers/api/Example.php:43
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/application/libraries/REST_Controller.php:654
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php:312
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php:256
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php:133
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestCase.php:106
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/application/tests/controllers/Uri_test.php:49
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/phpunit:36

FAILURES!
Tests: 8, Assertions: 6, Errors: 2.

Generating code coverage report in Clover XML format ... done

Generating code coverage report in HTML format ... done

とこんな感じでexitで終了してくれず、変数の値をうまく確認できません。

これは、ちょっと不便です。

そこで、今日は、PsySHを導入してデバッグを楽にします。

PsySHのインストール

require-devpsy/psyshを追加してcomposer updateします。

--- a/composer.json
+++ b/composer.json
@@ -9,6 +9,7 @@
         "mikey179/vfsStream": "1.1.*",
         "kenjis/ci-phpunit-test": "1.0.x@dev",
         "satooshi/php-coveralls": "0.6.*",
-        "phpunit/phpunit": "4.7.*"
+        "phpunit/phpunit": "4.7.*",
+        "psy/psysh": "0.5.*"
     }
 }

PsySHの動作には、最新のci-phpunit-test(2015/09/05以降の1.0.x@devまたはv0.7.0以降)が必要です。

デバッグの実際

デバッグのためにexitを記載していた箇所(ブレークポイント)に、代わりにeval(\Psy\sh());を記載します。

--- a/application/controllers/api/Example.php
+++ b/application/controllers/api/Example.php
@@ -40,7 +40,7 @@ class Example extends REST_Controller {
         ];

         $id = $this->get('id');
-
+eval(\Psy\sh());
         // If the id parameter doesn't exist return all the users

         if ($id === NULL)

そして、phpunitを実行します。

$ ../../vendor/bin/phpunit controllers/Uri_test.php 
PHPUnit 4.7.7 by Sebastian Bergmann and contributors.

......Psy Shell v0.5.2 (PHP 5.5.26 — cli) by Justin Hileman

ブレークポイントで止まり、PsySHが起動されました。

ここで、調べたい変数を入力すれば、値が表示されます。

$id
=> "1"
$users
=> [
     [
       "id" => 1,
       "name" => "John",
       "email" => "john@example.com",
       "fact" => "Loves coding",
     ],
     [
       "id" => 2,
       "name" => "Jim",
       "email" => "jim@example.com",
       "fact" => "Developed on CodeIgniter",
     ],
     [
       "id" => 3,
       "name" => "Jane",
       "email" => "jane@example.com",
       "fact" => "Lives in the USA",
       0 => [
         "hobbies" => [
           "guitar",
           "cycling",
         ],
       ],
     ],
   ]

変数の一覧も取得できます。

>>> ls
Variables: $__ret__, $id, $this, $users

$__ret__はモンキーパッチ機能が使っている特別な変数です。

関数を実行することもできます。

>>> get_class($this)
=> "Example"

オブジェクトのパブリックなプロパティの一覧。

>>> ls -p $this
Class Properties: $auth_override, $benchmark, $config, $form_validation, $format, $hooks, $input, $lang, $load, $loader, $output, $router, $security, $session, $uri

オブジェクトのすべてのプロパティの一覧。

>>> ls -p -a $this
Class Properties: $_allow, $_apiuser, $_args, $_delete_args, $_enable_xss, $_end_rtime, $_get_args, $_head_args, $_insert_id, $_options_args, $_patch_args, $_post_args, $_put_args, $_query_args, $_start_rtime, $_supported_formats, $_user_ldap_dn, $allowed_http_methods, $auth_override, $benchmark, $config, $form_validation, $format, $hooks, $http_status_codes, $input, $lang, $load, $loader, $methods, $output, $request, $response, $rest, $rest_format, $router, $security, $session, $uri

オブジェクトのパブリックなメソッドの一覧。

>>> ls -m $this
Class Methods: __construct, __destruct, _remap, delete, get, get_instance, head, options, patch, post, put, query, response, set_response, users_delete, users_get, users_post, validation_errors

バックトレース。

>>> trace
 0:  eval() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/application/controllers/api/Example.php:43
 1:  Example->users_get() at n/a:n/a
 2:  call_user_func_array() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/application/libraries/REST_Controller.php:654
 3:  REST_Controller->_remap() at n/a:n/a
 4:  call_user_func_array() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php:312
 5:  CIPHPUnitTestRequest->createAndCallController() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php:256
 6:  CIPHPUnitTestRequest->requestUri() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php:133
 7:  CIPHPUnitTestRequest->request() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestCase.php:106
 8:  CIPHPUnitTestCase->request() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/application/tests/controllers/Uri_test.php:43
 9:  Uri_test->test_api_example_users_uri() at n/a:n/a
10:  ReflectionMethod->invokeArgs() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/src/Framework/TestCase.php:881
11:  PHPUnit_Framework_TestCase->runTest() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/src/Framework/TestCase.php:746
12:  PHPUnit_Framework_TestCase->runBare() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/src/Framework/TestResult.php:601
13:  PHPUnit_Framework_TestResult->run() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/src/Framework/TestCase.php:702
14:  PHPUnit_Framework_TestCase->run() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/src/Framework/TestSuite.php:738
15:  PHPUnit_Framework_TestSuite->run() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/src/TextUI/TestRunner.php:428
16:  PHPUnit_TextUI_TestRunner->doRun() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/src/TextUI/Command.php:147
17:  PHPUnit_TextUI_Command->run() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/src/TextUI/Command.php:99
18:  PHPUnit_TextUI_Command::main() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/phpunit:36

オブジェクトのPHPDoc。

>>> doc $this
class Example extends REST_Controller

Description:
  This is an example of a few basic user interaction methods you could use
  all done with a hardcoded array

Package: CodeIgniter

Subpackage: Rest Server

Category: Controller

Author: Phil Sturgeon, Chris Kacerguis

License: MIT

Link: https://github.com/chriskacerguis/codeigniter-restserver

オブジェクトなどのソースコード。

>>> show $this::users_get
  >  33|     public function users_get()
     34|     {
     35|         // Users from a data store e.g. database
     36|         $users = [
     37|             ['id' => 1, 'name' => 'John', 'email' => 'john@example.com', 'fact' => 'Loves coding'],
     38|             ['id' => 2, 'name' => 'Jim', 'email' => 'jim@example.com', 'fact' => 'Developed on CodeIgniter'],
     39|             ['id' => 3, 'name' => 'Jane', 'email' => 'jane@example.com', 'fact' => 'Lives in the USA', ['hobbies' => ['guitar', 'cycling']]],
     40|         ];
     41| 
     42|         $id = $this->get('id');
     43| eval(\Psy\sh());
     44|         // If the id parameter doesn't exist return all the users
     45| 
     46|         if ($id === NULL)
     47|         {
     48|             // Check if the users data store contains users (in case the database result returns NULL)
     49|             if ($users)
     50|             {
     51|                 // Set the response and exit
     52|                 $this->response($users, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
     53|             }
     54|             else
     55|             {
     56|                 // Set the response and exit
     57|                 $this->response([
     58|                     'status' => FALSE,
     59|                     'error' => 'No users were found'
     60|                 ], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
     61|             }
     62|         }
     63| 
     64|         // Find and return a single record for a particular user.
     65| 
     66|         $id = (int) $id;
     67| 
     68|         // Validate the id.
     69|         if ($id <= 0)
     70|         {
     71|             // Invalid id, set the response and exit.
     72|             $this->response(NULL, REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code
     73|         }
     74| 
     75|         // Get the user from the array, using the id as key for retreival.
     76|         // Usually a model is to be used for this.
     77| 
     78|         $user = NULL;
     79| 
     80|         if (!empty($users))
     81|         {
     82|             foreach ($users as $key => $value)
     83|             {
     84|                 if (isset($value['id']) && $value['id'] === $id)
     85|                 {
     86|                     $user = $value;
     87|                 }
     88|             }
     89|         }
     90| 
     91|         if (!empty($user))
     92|         {
     93|             $this->set_response($user, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
     94|         }
     95|         else
     96|         {
     97|             $this->set_response([
     98|                 'status' => FALSE,
     99|                 'error' => 'User could not be found'
    100|             ], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
    101|         }
    102|     }

コマンドがわからないときはヘルプを表示しましょう。

>>> help
  help       Show a list of commands. Type `help [foo]` for information about [foo].      Aliases: ?                     
  ls         List local, instance or class variables, methods and constants.              Aliases: list, dir             
  dump       Dump an object or primitive.                                                                                
  doc        Read the documentation for an object, class, constant, method or property.   Aliases: rtfm, man             
  show       Show the code for an object, class, constant, method or property.                                           
  wtf        Show the backtrace of the most recent exception.                             Aliases: last-exception, wtf?  
  whereami   Show where you are in the code.                                                                             
  throw-up   Throw an exception out of the Psy Shell.                                                                    
  trace      Show the current call stack.                                                                                
  buffer     Show (or clear) the contents of the code input buffer.                       Aliases: buf                   
  clear      Clear the Psy Shell screen.                                                                                 
  history    Show the Psy Shell history.                                                  Aliases: hist                  
  exit       End the current session and return to caller.                                Aliases: quit, q      

確認が済んだら、[control]+[c]キーでphpunitを終了させましょう。

^C

という感じで、コマンドラインが好きな人にはPsySHをお薦めします。

関連

Date: 2015/09/04

Tags: phpunit, codeigniter, testing