Pages

11/22/2012

Using PHPSpec with CodeIgniter

Below is how I use PHPSpec together with CodeIgniter.

First, I create a folder called "phpspec" inside CodeIgniter folder, so I can put all spec files there.
Then, I created a file called "ci_bootstrap.php" almost as same as what's in "system/core/CodeIgniter.php" to initialize CodeIgniter.
Lastly, every test spec file is just needed to require "ci_bootstrap.php" and initialize CodeIgniter in "beforeAll()" before it can be used normally.

 File Structure:
/               (codeigniter base folder)
  + application
  + system
  - phpspec
    - ci_bootstrap.php
    - BranchSpec.php
    - ...

content of ci_bootstrap.php
<?php

/* CI_BootStrap by Anidear
 * 
 * This file is used for starting up CodeIgniter without calling Router.
 * After require/inclide this file, a Controller can be created by using:
 *   $class = 'main';
 *   include_once(APPPATH.'controllers/'.$class.'.php');
 *   $CI = new $class();
 * 
 * Model and Library can be loaded after getting a Controller, such as:
 *   $CI->load->model('Data_model');
 *   $CI->load->library('database');
 * 
 */

//disable E_STRICT in phpspec for codeigniter
error_reporting(E_ALL|~E_STRICT);


define('BASEPATH',realpath('../system/').'/'); //set absolute path to CI system/
define('APPPATH', '../application/'); //set relative path to CI application
set_include_path(
 get_include_path().PATH_SEPARATOR.
 realpath(APPPATH).PATH_SEPARATOR.
 realpath(BASEPATH).PATH_SEPARATOR.'phpspec');  //adding both path for short-name class calling./* If using
 *  - require_once(BASEPATH.'core/CodeIgniter.php');
 * or
 *  - require 'index.php';
 * It needs URL for Router to load specific class & method.
 * But we need to load it manually (by-pass Router)
 */

//load CI (manually)
require BASEPATH.'core/Common.php';
require APPPATH.'config/constants.php';
require BASEPATH.'core/Controller.php';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
function &get_instance() { return CI_Controller::get_instance(); }
$CFG =& load_class('Config', 'core');
$GLOBALS['CFG'] = $CFG;     //fix by Anidear
$UNI =& load_class('Utf8', 'core');
$URI =& load_class('URI', 'core');
//$RTR =& load_class('Router', 'core');  //bypass router
//$RTR->_set_routing();  //bypass router
$OUT =& load_class('Output', 'core');
$SEC =& load_class('Security', 'core');
$GLOBALS['SEC'] = $SEC;     //fix by Anidear
$IN =& load_class('Input', 'core');
$LANG =& load_class('Lang', 'core');

//load controller (by Router)
//include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php');


Then every test specification file would require this file at top and initialize CI (as in "beforeAll()"  ) before it can be used.

For example: content of BranchSpec.php
<?php
require 'ci_bootstrap.php';

class DescribeBranch extends \PHPSpec\Context {

 //this variable will be used as $this->model
 private $model = NULL;  

 public function beforeAll(){
  //load a controller
  $class = 'main';
  include_once(APPPATH.'controllers/'.$class.'.php');
  $CI = new $class();
  $GLOABALS['CI'] = $CI;  //assign CI as global variable

  //load a model
  $CI->load->model('Branch_model');
  $this->model = $CI->Branch_model;  //assign $this->model
 }
 public function itShouldReturnBranchesAsArray(){
  //$this->pending();
  $branches = $this->model->branches();
  $this->spec($branches)->should->beArray();
  $this->spec($branches)->shouldNot->beEmpty();
 }
}
Above file is for testing a model.

If you need to use any of  "$this->" functions, such as $this->db->get(...)."
A way to do it is to declare "global $CI;" and use "$CI->" instead of "$this-&gt", like this:
global $CI;
$CI->db ->where('branch_id',$branch_id)
 ->where('user_id',$user_id)
 ->where('`date`','2012-11-11')
 ->get('calendar');
Update:

  • I just found that PHPSpec has updated a method to install PHPSpec in a generic project. Although, I am not sure yet how it will have any effect on testing with CodeIgniter.
  • I just learned there are 2 versions of PHPSpec: PHPSpec and PHPSpec2. They are very different. Code from PHPSpec does not work with PHPSpec2. And the code in this blog post is based on PHPSpec (not PHPSpec2). So, if you use PHPSpec2, it will not work.
  • Installing PHPSpec can be done by PHP Composer, just very similar to PHPSpec2 in http://www.phpspec.net/ , except changing the line "phpspec/phpspec2": "*" to "phpspec/phpspec": "*"  Then run composer update to install PHPSpec.