Unable to use DataMapper ORM in test cases

Issue #11 closed
created an issue

When using a combination of CodeIgniter 2.1.0 + DataMapper ORM + the latest my-ciunit, as published by Kenji this week, and having models extend DataMapper instead of CI_Model, manual tests in the browser work correctly (CI + DmORM), but the unit tests cannot (CI + DmORM + my-ciunit).

When launching PHPUnit at the command line:

{{{ class Phone_carrier_model extends DataMapper { ... } }}}

would result in an error :

{{{ PHP Fatal error: Call to undefined method CI_DB_mysql_driver::dm_call_method() in (...)\application\libraries\datamapper.php on line 1113 }}}

CI_DB_mysql_driver is a native CodeIgniter class, whereas dm_call_method() is a DataMapper method.

It looks as if CIUnit should have loaded a DataMapper db class but didn't, maybe because link with the ORM is not "bootstraped" for the tests.

Comments (12)

  1. IhariR reporter

    Yes I already did and it gave yet another error, your idea being that as for the web app, the unit testing must be bootstrapped:

    PHP Fatal error:  Call to undefined method DM_Loader::reset() in D:\DataFiles\www\_tests\CI-2.1.0+DmORM-\application\third_party\CIUnit\libraries\CIUnit.php on line 77

    For this error as well as for the former, the unfound method does not belong to the class, so obviously the wrong loader is being used, or that methods in CI_Loader, CIU_Loader and DM_Loader need to be somehow "merged" by having CIU_Loader extending DM_Loader extending CI_Loader (?).

    The possible culprit or starting point may then be the following line in application\third_parties\CIUnit\libraries\CIUnit.php:

    $loader =& load_class('Loader', 'core');

    But changing this or playing with the &load_class() method int application\third_parties\CIUnit\core\Common.php just brings up an error saying that it is unable to locate the specified class.

    Another thing I did was to look for classes and methods in my-ciunit that might access the database : this is the case for application\third_parties\CIUnit\librairies\Fixtures.php with its load(), unload() and _assign_db() methods, BUT, the loader problem above seems to occur first and does not give the chance for these latter methods to coded, exectued and be tested in the DataMapper ORM way.

  2. Kenji repo owner
    • changed status to open

    The error message shows the following code loads DM_Loader. But it should load CI_Loader.

    $loader =& load_class('Loader', 'core');

    Fixture is the functionality to load data for testing to your database before running tests. You can use it in your database tests, but you don't have to use it. After all, loading problem must be resolved first.

  3. IhariR reporter

    You mean CIU_Loader ? given that the method whis is called immediately afterwards is reset() and it is a method of your CIU_Loader, not that of the original CI_Loader:

    // the current controller must be archieved before littered
    $loader =& load_class('Loader', 'core');
    // reset all loaded data
  4. IhariR reporter

    Putting aside the CIU_Loader issue, this it a very dirty workaround that works, and which is virtually (i.e. unless I forgot something along the way) the only modification I did to the code in order for it to work:

    Given that the problem is "Call to undefined method CI_DB_mysql_driver::dm_call_method()" but given that this method is introduced by DM_DB_Driver, a derivative of CI_DB_Driver (a.k.a DB_Driver in DB_Driver.php), I directly went to this latter file and added the method definition:

    // system\database\DB_driver.php
    public function dm_call_method($function, $p1 = null, $p2 = null, $p3 = null, $p4 = null)
    	switch (func_num_args())
    		case 1:
    			return $this->{$function}();
    		case 2:
    			return $this->{$function}($p1);
    		case 3:
    			return $this->{$function}($p1, $p2);
    		case 4:
    			return $this->{$function}($p1, $p2, $p3);
    		case 5:
    			return $this->{$function}($p1, $p2, $p3, $p4);

    This allows phpunit tests to be executed and the web application to continue to run.

  5. Anonymous

    Sigh ... I just spent a good 4 hours on this issue trying to find a better workaround than modifying the core DB file directly. As it turns out, this solution is just as simple as the workaround.

    All you need to do is copy over DataMapper's database loader function. Open up CIU_Loader and add this function to the end:

        public function database($params = '', $return = FALSE, $active_record = NULL)
            //parent::database($params, $return, $active_record);
            // Grab the super object
            $CI =& get_instance();
            // Do we even need to load the database class?
            if (class_exists('CI_DB') AND $return == FALSE AND $active_record == NULL AND isset($CI->db) AND is_object($CI->db))
                return FALSE;
            if ($return === TRUE)
                return DB($params, $active_record);
            // Initialize the db variable.  Needed to prevent
            // reference errors with some configurations
            $CI->db = '';
            // Load the DB class
            $CI->db =& DB($params, $active_record);
  6. Anonymous

    Note that this code is exactly the same as the code in third_party/datamapper/system/Loader.php file, minus the string eval.

    Also note that my code uses DATAMAPPERPATH. This is because I use the sparks version and set it up as described in this post: http://codeigniter.com/forums/viewthread/205637/P90

    If you use DataMapper-ORM without sparks, you'll probably use APPPATH instead.

  7. Log in to comment