[eluser]ch5i[/eluser]
Hello,
after a lot of trial & error I got this finally to work.
Thanks for your post Aniket, that got me started.
So, here it is: A simple example on how to mock the CI database class.
User model (test subject)
Code:
<?php
/**
* User_model
*/
class User_model extends Model {
function User_model()
{
parent::__construct();
}
public function getAllUsers()
{
return $this->db->get('users');
}
public function add($firstname)
{
$usr = new stdClass();
$usr->firstname = $firstname;
return $this->db->insert('users', $usr);
}
}
Unit Test Case
Code:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
class TestMockClass extends UnitTestCase{
// holds reference to ci instance
private $_ci = NULL;
public function __construct()
{
$this->UnitTestCase("Mock object demo 2");
// fetch ref to ci instance
$this->_ci =& get_instance();
// generate a class that has the same interface as
// CI's currently loaded db
Mock::generate(get_class($this->_ci->db), 'fake_DB');
// generate a class with the same interface as CI_DB_result
require_once(BASEPATH.'database/DB_result'.EXT);
Mock::generate('CI_DB_result', 'fake_DB_result');
// load the test subject (user model)
$this->_ci->load->model('user_model');
}
public function setUp()
{
// replace CI's db with the faked one
$this->_ci->db = new fake_DB();
// THIS MAKES IT WORK
// explicitly replace the db object
// in the scope of the test subject
// to get the references working...
$this->_ci->user_model->db = $this->_ci->db;
}
public function tearDown()
{
// not needed
// unset($this->_ci->db);
}
public function testAddNewUser()
{
// the 'add' method in user_model calls
// the 'insert' method of CI's db object ($this->db->insert()),
// which we replaced with the fake (mocked) one.
// It has the same interface, but no implementation for the methods
// so we have to tell it what to return for a given method call
//
// array('users', '*') means first param of insert must
// be 'users' (the tablename), the second can be whatever
$this->_ci->db->setReturnValue(
'insert',
TRUE,
array('users', '*')
);
$this->assertTrue($this->_ci->user_model->add('Jack'), '%s');
}
public function testGetDbResult()
{
// create some dummy data to return later
$jim = new stdClass();
$jim->firstname = 'Jim';
$jim->id = 1;
$joe = new stdClass();
$joe->firstname = 'Joe';
$joe->id = 4;
$result = array($jim, $joe);
// set the query to mock ci_db_result
// we want to get a mock db_result
// where we can control what comes out of our
// 'virtual' database
$query = new fake_DB_result();
// set the return value for the num_rows() method
$query->setReturnValue('num_rows', 2);
// set the return value for the row(2) method call
$query->setReturnReference('row', $joe, array(2));
// set the return value for the result() method call
$query->setReturnReference('result', $result);
// set the return value for db->get('users') method call
$this->_ci->db->setReturnReference(
'get',
$query,
array('users')
);
// implicitly call the db->get() method (in user_model)
$returned_query = $this->_ci->user_model->getAllUsers();
$this->assertReference($query, $returned_query);
// call num_rows() on the returned (fake) db_result
$this->assertEqual($returned_query->num_rows(), 2);
// call row(2) on the returned (fake) db_result
$this->assertEqual($returned_query->row(2)->firstname, 'Joe');
// call result() method on the returned (fake) db_result
foreach($returned_query->result() as $user) {
$this->assertPattern('#J.{2}#', $user->firstname);
}
}
}
Sidenote:
I use SimpleTest, set up as described in the
Wiki.
Well, almost as described, I do not autoload the simpletester library (step 5), but have a unit test controller which loads it and calls the Run() method, so that the tests are not run everytime i call a controller.