PHP Developers Network

A community of PHP developers offering assistance, advice, discussion, and friendship.
 
Loading
It is currently Mon Sep 24, 2018 11:36 am

All times are UTC - 5 hours




Post new topic Reply to topic  [ 10 posts ] 
Author Message
PostPosted: Tue Aug 02, 2005 8:41 pm 
Offline
DevNet Master
User avatar

Joined: Mon Oct 25, 2004 9:29 pm
Posts: 3698
Location: New Jersey, US
I've been working with them and already I'm having problems emulating what the actual function is supposed to do. For instance, take this call:

Syntax: [ Download ] [ Hide ]
$array = array();

            while($res->fetchInto($row, DB_FETCHMODE_ASSOC)) {

                $array[$row['id']] = $row;

            }


The function sets $row as something because it was passed by reference. How the heck do you do that in SimpleTest (yes, I read the docs. Do I have to extend the class?)


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 02, 2005 9:37 pm 
Offline
DevNet Resident
User avatar

Joined: Fri Aug 16, 2002 8:57 am
Posts: 1834
Location: Watertown, MA
While we wait for the expert (McGruff) :)

Let me make sure I understand what you're doing.

You have a ResultSet fo come kind. When you want to get the next row, you have it return a reference to the row. (Probably because you wanted a pure boolen true/false return from the function?)

You are now trying to mock the ResultSet and are having difficulties getting it to give you a reference back.

I thought maybe the SimpleStub base class would help out, but its setReturnReference appears to only be useful for returning a reference as the return value, not as an argument. So I think you're right and that you'll need to do something a little different.

I'ld try emailing the simpletest email list. Marcus is great about suggesting simple workarounds....


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 02, 2005 10:50 pm 
Offline
DevNet Master

Joined: Thu Jan 30, 2003 9:26 pm
Posts: 2893
Location: Glasgow, Scotland
Can you post the test method?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 03, 2005 4:49 pm 
Offline
DevNet Master
User avatar

Joined: Mon Oct 25, 2004 9:29 pm
Posts: 3698
Location: New Jersey, US
I have to warn you, I've changed the method since I last posted my question, particularly I changed the function to one with a more conventional method of returning results.

Syntax: [ Download ] [ Hide ]
$this->obj->db =& new MockDB_mysql($this);

        $result_email =& new MockDB_result_email($this);

        $result_ftp =& new MockDB_result_ftp($this);

       

        $result_email->setReturnValue('fetchRow', false);

        $result_email->setReturnValueAt(0, 'fetchRow',

          array(

            'id' => 4

            ,'address' => 'arcezy@gmail.com'

            ,'last_md5' => '072734bca324930cf0c2e48710425814'

          ));

       

        $result_ftp->setReturnValue('fetchRow', false);

        $result_ftp->setReturnValueAt(0, 'fetchRow',

          array(

            'id' => 2

            ,'server' => 'www.thewritingpot.com'

            ,'username' => 'backup'

            ,'password' => 'pass'

            ,'max_copies' => 'max_copies'

            ,'last_md5' => '072734bca324930cf0c2e48710425814'

          ));

       

        $this->obj->db->setReturnValue('query', &$result_email, array(' SELECT `id`, `interval`, `last_md5`  FROM `backup_email`  WHERE ((NOW() - `interval`) >

      `last_update`) '
));

        $this->obj->db->setReturnValue('query', &$result_ftp, array(' SELECT `id`, `interval`, `last_md5`  FROM `backup_ftp`  WHERE ((NOW() - `interval`) >

      `last_update`) '
));

       

        $this->obj->getOperations();

       

        //var_dump($this->obj->list_backups);

       

        $this->assertEqual($this->obj->list_backups['backup_email'][4]['id'],4);

        $this->assertEqual($this->obj->list_backups['backup_email'][4]['address'],'arcezy@gmail.com');


Unfortunantely, I'm not very happy with this test case either, because the set return values are inflexible for varying SQL queries. Since the query is fairly constant with only a few variations, I'm considering moving it to its own function, but then I'd have problems mocking it.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 03, 2005 8:41 pm 
Offline
DevNet Master

Joined: Thu Jan 30, 2003 9:26 pm
Posts: 2893
Location: Glasgow, Scotland
Setting object properties without named setters is cheating a bit. An object should have a well-defined interface.

For example you could pass the db object in to the primary class (ie the one being tested). Or, if you don't want the db object exposed in the design like that, you can use a factory method and partially mock in the test (the procedure is explained in the SimpleTest docs - but do ask if you'd like me to explain).

Either way, rather than setting expectations for the mock db objectlike this:
Syntax: [ Download ] [ Hide ]
$this->obj->db->setReturnValue(...)

..just do this:
Syntax: [ Download ] [ Hide ]
$db =& new MockDB_mysql($this);

$db->setReturnValue(...)

$db->setReturnValue(...)


..then pass it in or set as a return ref for the partially mocked method.

You probably ought to set some expectOnce/expectCallCount expectations as well (don't forget to tally up: $mock_object->tally() ).

Assertions like this;
Syntax: [ Download ] [ Hide ]
$this->assertEqual($this->obj->list_backups['backup_email'][4]['id'],4);

..aren't good because you're peeking under the bonnet. Is there another way to test if the getOperations call did what it was supposed to do? if all it's doing is teeing up the class I'd be tempted to do that in the constructor - maybe you can test another method of the primary class which actually does something? (I might not have grasped what you're doing exactly).


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 04, 2005 6:04 pm 
Offline
DevNet Master
User avatar

Joined: Mon Oct 25, 2004 9:29 pm
Posts: 3698
Location: New Jersey, US
McGruff wrote:
Setting object properties without named setters is cheating a bit. An object should have a well-defined interface.

For example you could pass the db object in to the primary class (ie the one being tested). Or, if you don't want the db object exposed in the design like that, you can use a factory method and partially mock in the test (the procedure is explained in the SimpleTest docs - but do ask if you'd like me to explain).


Ah... I think I understand. So it's a good idea not to use $this-> variables as things you stick variables in for easy access from functions? Edit No, I was mistaken, it's not a good idea to directly set object properties, there should always be a method above it.

McGruff wrote:
Either way, rather than setting expectations for the mock db objectlike this:
Syntax: [ Download ] [ Hide ]
$this->obj->db->setReturnValue(...)

..just do this:
Syntax: [ Download ] [ Hide ]
$db =& new MockDB_mysql($this);
$db->setReturnValue(...)
$db->setReturnValue(...)


..then pass it in or set as a return ref for the partially mocked method.


That makes sense. I suppose it's cleaner.

Quote:
You probably ought to set some expectOnce/expectCallCount expectations as well (don't forget to tally up: $mock_object->tally() ).


Hmm... I'll incorporate those too.

Quote:
Assertions like this;
Syntax: [ Download ] [ Hide ]
$this->assertEqual($this->obj->list_backups['backup_email'][4]['id'],4);

..aren't good because you're peeking under the bonnet. Is there another way to test if the getOperations call did what it was supposed to do? if all it's doing is teeing up the class I'd be tempted to do that in the constructor - maybe you can test another method of the primary class which actually does something? (I might not have grasped what you're doing exactly).


Hmm... I think I need to post more code. Originally, getOperations cycled through an array of table names and performed a specific query on each of them. I suppose, then, that getOperations should be simply the cycling through the array, and then an alternate function/class handles the sql.

Edit: I just reread the section on Partial Mocks, and it's really interesting. However, I'm a bit worried about the last section:

Quote:
We could even go as far as to mock every method except one we actually want to test.

This last situation is all rather hypothetical, as I haven't tried it. I am open to the possibility, but a little worried that forcing object granularity may be better for the code quality. I personally use partial mocks as a way of overriding creation or for occasional testing of the TemplateMethod pattern.

It's all going to come down to the coding standards of your project to decide which mechanism you use.


I'm not exactly sure what mechanism I should use (this is a pretty small project that's meant to be deployed only on one machine but maybe be deployed on a wider scale and I want to be flexible but I also want to get it finished.) The reason why I turned to unit testing was that I was throwing variables around until the properties table was a huge mess and I couldn't figure out where I stored any particular bit of information. I'm not even sure if I should be unit testing this sort of thing...


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 04, 2005 10:32 pm 
Offline
DevNet Master

Joined: Thu Jan 30, 2003 9:26 pm
Posts: 2893
Location: Glasgow, Scotland
Do you mean you're not sure whether to pass the $db object in or to instantiate in a factory method?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 5:08 pm 
Offline
DevNet Master
User avatar

Joined: Mon Oct 25, 2004 9:29 pm
Posts: 3698
Location: New Jersey, US
Yeah, basically.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 5:41 pm 
Offline
Forum Commoner

Joined: Sat Jul 12, 2003 10:31 pm
Posts: 80
Location: London
Hi...

This forum does seem to have taken off since I last looked :).

I am afraid I don't have the time to monitor another forum, but if there are queries about the innards of SimpleTest here, feel free to mail the thread link to the SimpleTest mail list or to me and I'll try to reply here.

Regarding emulating result sets, I tend to do this...
Syntax: [ Download ] [ Hide ]
$iterator = &new MockResultSet();

$iterator->setReturnValueAt(0, 'next', array(...));

$iterator->setReturnValueAt(1, 'next', array(...));



$connection = &new MockConnection();

$connection->setReturnReference('query', $iterator);

If that doesn't work then I will extend the mock class or even hand code a mock for the task. I guess as that's the way I started I am more willing than most to hand code when SimpleTest doesn't do what I want.

yours, Marcus

p.s. No I didn't miss off the $this ;). It's unnecessary in the current CVS code. The tally() call is deprecated too as it's now automatic.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 7:06 pm 
Offline
DevNet Master

Joined: Thu Jan 30, 2003 9:26 pm
Posts: 2893
Location: Glasgow, Scotland
OK in general, I'd say you wouldn't want to expose an object in the design any more than you have to. If object foo is the only one which uses object bar, instantiate it in a factory method - then partially mock the factory method in foo so you can supply it with a mock bar in tests.

On the other hand, if other objects in the script are also using the bar object, you'll be passing it in either in the constructor or one of the foo methods. Pass in a mock in the tests.

With database objects, it's quite likely that they will be used in a variety of places in the script. Sometimes a Registry is used to save passing around frequently used objects like this all over the place. In tests, set the Registry up to return a mock object.

Strictly speaking, if you are test-driven-designing, you wouldn't add a Registry into the mix until a need for one emerges (XP emphasises doing just what you need for the current requirement and no more). You should be prepared to read between the lines a little bit though if you are sure you will need one later.

So, take your pick... :)


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 10 posts ] 

All times are UTC - 5 hours


Who is online

Users browsing this forum: No registered users and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group