Skip to content

Latest commit

 

History

History
310 lines (239 loc) · 8.59 KB

File metadata and controls

310 lines (239 loc) · 8.59 KB

CI PHPUnit Test for CodeIgniter 3.0

How to Write Tests

Models

Using Database

tests/models/Inventory_model_test.php

<?php

class Inventory_model_test extends TestCase
{
	public function setUp()
	{
		$this->CI =& get_instance();
		$this->CI->load->model('shop/Inventory_model');
		$this->obj = $this->CI->Inventory_model;
	}

	public function test_get_category_list()
	{
		$expected = [
			1 => 'Book',
			2 => 'CD',
			3 => 'DVD',
		];
		$list = $this->obj->get_category_list();
		foreach ($list as $category) {
			$this->assertEquals($expected[$category->id], $category->name);
		}
	}

	public function test_get_category_name()
	{
		$actual = $this->obj->get_category_name(1);
		$expected = 'Book';
		$this->assertEquals($expected, $actual);
	}
}

Test case class extends TestCase class.

Don't forget to write parent::setUpBeforeClass(); if you override setUpBeforeClass() method.

See working sample.

Database Seeding

I put Seeder Library and a sample Seeder File.

They are not installed, so if you want to use, copy them manually.

You can use them like below:

	public static function setUpBeforeClass()
	{
		parent::setUpBeforeClass();

		$CI =& get_instance();
		$CI->load->library('Seeder');
		$CI->seeder->call('CategorySeeder');
	}

See working sample.

Using PHPUnit Mock Objects

You can use $this->getMockBuilder() method in PHPUnit and $this->verifyInvoked*() helper methods in CI PHPUnit Test.

	public function setUp()
	{
		$this->CI =& get_instance();
		$this->CI->load->model('Category_model');
		$this->obj = $this->CI->Category_model;
	}

	public function test_get_category_list()
	{
		// Create mock objects for CI_DB_pdo_result and CI_DB_pdo_sqlite_driver
		$return = [
			0 => (object) ['id' => '1', 'name' => 'Book'],
			1 => (object) ['id' => '2', 'name' => 'CD'],
			2 => (object) ['id' => '3', 'name' => 'DVD'],
		];
		$db_result = $this->getMockBuilder('CI_DB_pdo_result')
			->disableOriginalConstructor()
			->getMock();
		$db_result->method('result')->willReturn($return);
		$db = $this->getMockBuilder('CI_DB_pdo_sqlite_driver')
			->disableOriginalConstructor()
			->getMock();
		$db->method('get')->willReturn($db_result);
		
		// Verify invocations
		$this->verifyInvokedOnce(
			$db_result,
			'result',
			[]
		);
		$this->verifyInvokedOnce(
			$db,
			'order_by',
			['id']
		);
		$this->verifyInvokedOnce(
			$db,
			'get',
			['category']
		);

		// Replace property db with mock object
		$this->obj->db = $db;

		$expected = [
			1 => 'Book',
			2 => 'CD',
			3 => 'DVD',
		];
		$list = $this->obj->get_category_list();
		foreach ($list as $category) {
			$this->assertEquals($expected[$category->id], $category->name);
		}

		// Reset CI object for next test case, unless property db won't work
		reset_instance();
		new CI_Controller();
	}

See working sample.

Controllers

Request to Controller

You can use $this->request() method in CI PHPUnit Test.

tests/controllers/Welcome_test.php

<?php

class Welcome_test extends TestCase
{
	public function test_index()
	{
		$output = $this->request('GET', ['Welcome', 'index']);
		$this->assertContains('<title>Welcome to CodeIgniter</title>', $output);
	}
}

See working sample.

Request to URI string

	public function test_uri_sub_sub_index()
	{
		$output = $this->request('GET', 'sub/sub/index');
		$this->assertContains('<title>Page Title</title>', $output);
	}

See working sample.

Ajax Request

You can use $this->ajaxRequest() method in CI PHPUnit Test.

	public function test_index_ajax_call()
	{
		$output = $this->ajaxRequest('GET', ['Ajax', 'index']);
		$expected = '{"name": "John Smith", "age": 33}';
		$this->assertEquals($expected, $output);
	}

See working sample.

Examine DOM in Controller Output

I recommend to use symfony/dom-crawler.

		$output = $this->request('GET', ['Welcome', 'index']);
		$crawler = new Symfony\Component\DomCrawler\Crawler($output);
		// Get the text of the first <h1>
		$text = $crawler->filter('h1')->eq(0)->text();

See working sample.

Controller with Authentication

I recommend to use PHPUnit mock objects. $this->getDouble() is a helper method in CI PHPUnit Test.

	public function test_index_logged_in()
	{
		$inject_ion_auth = function ($CI) {
			// Get mock object
			$auth = $this->getDouble(
				'Ion_auth', ['logged_in' => TRUE, 'is_admin' => TRUE]
			);
			// Inject mock object
			$CI->ion_auth = $auth;
		};
		$output = $this->request('GET', ['Auth', 'index'], [], $inject_ion_auth);
		$this->assertContains('<p>Below is a list of the users.</p>', $output);
	}

See working sample.

redirect()

I recommend to use this MY_url_helper.php.

If you use it, you can write tests like this:

	/**
	 * @expectedException				PHPUnit_Framework_Exception
	 * @expectedExceptionCode			302
	 * @expectedExceptionMessageRegExp	!\ARedirect to http://localhost/login\z!
	 */
	public function test_index()
	{
		$output = $this->request('GET', ['Admin', 'index']);
	}

See working sample.

Controller with Name Collision

If you have two controllers with the exact same name, PHP Fatal error stops PHPUnit testing.

In this case, you can use PHPUnit annotations @runInSeparateProcess and @preserveGlobalState disabled. But tests in a separate PHP process are very slow.

tests/controllers/sub/Welcome_test.php

<?php

class sub_Welcome_test extends TestCase
{
	/**
	 * @runInSeparateProcess
	 * @preserveGlobalState disabled
	 */
	public function test_uri_sub_welcome_index()
	{
		$output = $this->request('GET', 'sub/welcome/index');
		$this->assertContains('<title>Page Title</title>', $output);
	}
}

See working sample.

show_error() and show_404()

show_error() and show_404() in CI PHPUnit Test throw PHPUnit_Framework_Exception.

	/**
	* @expectedException		PHPUnit_Framework_Exception
	* @expectedExceptionCode	404
	*/
	public function test_index()
	{
		$output = $this->request('GET', ['nocontroller', 'noaction']);
	}

See working sample.

Mock Libraries

You can put mock libraries in tests/mocks/libraries folder. You can see application/tests/mocks/libraries/email.php as a sample.

With mock libraries, you could replace your object in CodeIgniter instance.

This is how to replace Email library with Mock_Libraries_Email class.

	public function setUp()
	{
		$this->CI =& get_instance();
		$this->CI->load->model('Mail_model');
		$this->obj = $this->CI->Mail_model;
		$this->CI->email = new Mock_Libraries_Email();
	}

Mock library class name must be Mock_Libraries_*, and it is autoloaded.

More Samples

Want to see more tests?