tags:

views:

25

answers:

2

While testing a piece of code(written as per codeigniter mvc) with PHPUnit i got the error PHP Fatal error: Call to a member function getPermissionObject() on a non-object. The code is as below and the part of it producing error is indented.

    class Some_model extends Model 
{

var $userid;
var $permisson_object;
var $username;
var $userType;
var $userFirstName;
 function Some_model()
 {
  parent::Model();
  $user_obj = @unserialize($this->db_session->userdata('user_object')); 

           //following line shows error
                     $this->permisson_object = $user_obj->getPermissionObject();

            $this->userid = $user_obj->getUserid();
  $this->username = $user_obj->getUsername();
  $this->userType =$user_obj->getusertype();
  $this->userFirstName = $user_obj->getFirstName()." ".$user_obj->getLastName();
 }
}

the error came because while testing i am unable to generate $user_obj from my tetclass.( As it is generated from seesion data)

my requirement is to generate the same in the test Class so that i can use it.

if any body has a solution for this please do share.

thanks

A: 

One possible answer to your problem is "Dependency Injection", frankly the one i like best. Just plain mocking is also possible.

Some expamples

<?php 
// You could pass the session into the object while constructing it
// so in your test you can pass in a "fake" user object (see below)
class Some_model extends Model {
function Some_model($user_object) {
    $user_object->yourFunctions();
}

this way you don't have to rely on some global state beeing set up the right way.

If that doesn't work for you ( i don't know codeigniter really well ) you could maybe to it like this:

<?php 
class Some_model extends Model {
function Some_model($user_object=null) {
    if($user_obj === null) {
        $user_obj = @unserialize($this->db_session->userdata('user_object')); 
    }
    $user_object->yourFunctions();
}

so in your tests you pass in a mocked user object ( a fake instance you created, for mocking see the docs) and your normal code doesn't have to change.

Also a good read: Slides about PHPUnit best practices from the PHPUnit Author. Hopefully the help clarify what i was trying to explain :)

Edit

sidhartha asked for some help with the mocking so i wrote some more demo code. I've stripped out most things to provide a full executable example as i guess "code you can run" is more helpful then anything else.

Let's say this is the code you want to test (reduced to the userid so it's shorter)

<?php
class Some_model extends Model 
{

    var $userid;
    var $permisson_object;

    function Some_model()
    {
        parent::Model();
        $user_obj = @unserialize($this->db_session->userdata('user_object')); 
        //following line shows error
        $this->permisson_object = $user_obj->getPermissionObject();
        $this->userid = $user_obj->getUserid();
     }

 }

Working with what i have tried to explain you could build a test like this one (everything in one file to make copy paste eaiser, of course one would split it)

class Model { public function Model() {}  }

class Some_model extends Model
{

    var $userid;
    var $permisson_object;

    function Some_model($user_obj)
    {
        parent::Model();
        //following line shows error
        $this->userid = $user_obj->getUserid();
        $this->permisson_object = $user_obj->getPermissionObject();
     }

    public function getThatUserid() {
        // Just to show off how mocking works a little bit better :)
        return $this->userid;
    }

}

class Permissions {
    public function getPermissions() {
        // lots of code here maybe
        return array("something from" => "the database");
    }
}

class User {
    public function getUserid() {
        // code code
        return 1;
    }
    public function getPermissionObject() {

    }
}



class Some_modelTest extends PHPUnit_Framework_Testcase {

    public function testConstruction() {
        $mockUser = $this->getMock("User");
        $mockPermissions = $this->getMock("Permission");

        $mockUser->expects($this->once())
                 ->method("getUserid")
                 ->will($this->returnValue(100));
#        $mockPermissions->expects($this->once())
#                        ->method("getPermissions");

        // Now for the "magic"
        $mockUser->expects($this->once())
                 ->method("getPermissionObject")
                 ->will($this->returnValue($mockPermissions));
         // ^^ now we are all set and user_obj is independen of the permissons object
        // Now pass the "Fake" user into the model
        $model = new Some_model($mockUser);
        // and just to show off the "will return value stuff"
        $this->assertSame(100, $model->getThatUserid());
    }
}

phpunit Some_modelTest.php PHPUnit 3.4.15 by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 4.25Mb

OK (1 test, 3 assertions)

I really hope that helps. It's a lot of code but as a demo it should work for you.

edorian
A: 

hi Edorian, thanks for your reply

but i am clueless.the object looks something like this

User_library Object
(
    [username:User_library:private] => [email protected]
    [firstname:User_library:private] => some
    [lastname:User_library:private] => Staff
    [typename:User_library:private] => 
    [userid:User_library:private] => 2
    [userType:User_library:private] => 2
    [permissionObject:User_library:private] => Permission_library Object
        (
            [create_custom_attr:Permission_library:private] => 1
            [create_jobspecific_attr:Permission_library:private] => 0
            [injob:Permission_library:private] => Array
                (
                    [products] => 2
                    [services] => 2
                    [options] => 2
                    [tasks_assigned_to_me] => 2
                    [tasks_assigned_to_others] => 2
                )

            [outjob:Permission_library:private] => Array
                (
                    [products] => 2
                    [services] => 2
                    [options] => 2
                    [tasks_assigned_to_others] => 2
                )

            [samedept:Permission_library:private] => Array
                (
                )

            [alldepts:Permission_library:private] => Array
                (
                    [add_and_assign_tasks] => 4
                    [add_and_assign_milestones] => 4
                )

        )

i do not find any option to mock this. because this object also has other objects in it. here User_library Object is the object of a class User_library. this also has Permission_library Object inside it. how can i mock the above by the following way

$observer = $this->getMock('Some_model', array('Some_model'));

        $observer->expects($this->any())
                 ->method('Some_model')
                 ->with($this->equalTo('something'));

here my problem is what would be there in the something part in $this->equalTo('something'). please guide me.

sidhartha
a little advice on the side: usually it's better if you edit your question so other people get the full picture quicker, i'll edit my answer soon to include more demo code
edorian