CodeIgniter のユニットテストクラスのテストを CIUnit から実行する

CodeIgniter には標準でユニットテストクラスが用意されています。

CodeIgniter のコードの書き方そのままにユニットテストが書け、機能もあまりないので比較的お手軽にテストコードを作成することができます。

ただし、低機能なゆえの限界が見えており、PHPUnit にはやはり及びません。

ユニットテストクラスのテストコード

例えば、テストコードは以下のようになり、実行はブラウザからこのコントローラにアクセスすることになります。

application/controllers/tests/test_form_validation_non_exist_rule.php

<?php

class Test_form_validation_non_exist_rule extends CI_Controller {
	function __construct()
	{
		parent::__construct();
		$this->load->library('unit_test');
	}

	function index()
	{
		$this->test_non_existence_rule();
		echo $this->unit->report();
	}
	
	function test_non_existence_rule()
	{
		//var_export($_POST);
		$_POST = array (
					'text-to-test' => 'hello world!',
				 );
		
		$this->load->library('form_validation');

		$this->form_validation->set_rules('text-to-test', 'Text to test' , 'not_exist|asd');

		$test = $this->form_validation->run();
		$expected = FALSE;
		$this->unit->run($test, $expected, __METHOD__, "$test -> $expected");
	}

}

/* End of file test_form_validation_non_exist_rule.php */
/* Location: ./system/application/controllers/tests/test_form_validation_non_exist_rule.php */

テストランナーもなくテストを実行しづらいという問題があり、CIUnit からユニットテストクラスのテストを実行できるようにしてみます。

ユニットテストクラスの改造

まず、ユニットテストクラスを改造します。Unit_test.php の report() メソッドを変更し、コマンドラインから実行した場合、出力をコマンドライン用に変更するようにします。

この改造は以下を参考にしました。というか、ほとんどそのままで、微妙に趣味の問題で出力フォーマットが違うくらいです。

なお、制限として、index() メソッドですべてテストを実行し結果を出力するように、テストを記述する必要があります。

PHPUnit でのテストコード

次に、ユニットテストクラスのテストを実行するテストコードを PHPUnit で以下のように作成します。

tests/controllers/CI_Unit_Test_class_Test.php

<?php

/**
 * @group Controller
 */

class CI_Unit_Test_class_Test extends CIUnit_TestCase
{
	public function setUp()
	{
	}
	
	public function test_CI_Unit_Test_Class()
	{
		$test_path = APPPATH . 'controllers';
		$test_folder = 'tests';
		
		$has_failed = FALSE;
		
		foreach (glob("$test_path/$test_folder/*.php") as $filename)
		{
			$filename = basename($filename, '.php');
			$this->CI = set_controller("$test_folder/$filename");
			
			echo "\nCI Unit Testing Class: $test_folder/$filename\n";
			if ($this->_run_ci_unit_test() === FALSE)
			{
				$has_failed = TRUE;
			}
		}
		
		if ($has_failed)
		{
			$this->fail();
		}
	}
	
	function _run_ci_unit_test()
	{
		// Output buffering
		ob_start();
		
		// Call the controllers method
		$this->CI->index();
		
		// Fetch the buffered output
		$output = ob_get_contents();
		
		// Clear buffer
		ob_end_clean();
		
		//echo 'output: ', $output;

		$has_failed = FALSE;
		$lines = explode("\n", $output);
		foreach ($lines as $line)
		{
			$all = 0;
			$passed = 0;
			$failed = 0;
			
			//echo $line, "\n";
			
			if (preg_match('/All:(\d) Passed:(\d) Failed:(\d)/', $line, $matches))
			{
				$all = $matches[1];
				$passed = $matches[2];
				$failed = $matches[3];
				
				if ($failed > 0)
				{
					$has_failed = TRUE;
				}
			}
			else if (substr($line, 0 ,1) === ' ')	// failed test name
			{
				echo "\x1b[31m" . $line . "\x1b[0m" . "\n";
			}
		}
		
		
		if ($has_failed)
		{
			return FALSE;
		}
		else
		{
			return TRUE;
		}
	}
}

ここでは、

		$test_path = APPPATH . 'controllers';
		$test_folder = 'tests';

で指定したフォルダ、この場合は「application/controllers/tests」の中の *.php ファイル(コントローラ)をすべて順に実行していきます。

実行結果

普通に phpunit を実行すれば、テストが実行されます。

ユニットテストクラスのテストは実行したファイル名と、失敗したテストの場合、赤字でテスト名を表示するようにしています。

また、失敗したテストがあった場合は、PHPUnit が 1 failure を報告するようにしてあります。本当はユニットテストクラスのテストの成功数と失敗数を PHPUnit に渡したかったのですが、やり方がわからなかったので。:-)

この記事で紹介したファイルは、すべて以下のリポジトリの CIUnit に含まれています。

上記 CIUnit は、現在、CodeIgniter 2.0.3 以上のみ動作します。たぶん、2.0.2 以前でも system/core/Common.php を 2.0.3 のものにすれば動くとは思いますが。