Page 3 of 3
Posted: Thu Aug 17, 2006 5:35 pm
by Chris Corbyn
Maybe I'm missing something, but I've scanned over the Mock Objects page (mock_objects_documentation.html) a few times now and I see no mention of why this behaviour happens.
I need to create a Mock of a FakePlugin class so that I can track the number of times methods are called, when they are called and possibly some other things.
I wrote this class, which is a skeleton of a Swift plugin:
Code: Select all
<?php
class FakePlugin implements Swift_IPlugin
{
public $pluginName = 'Fake';
private $swift;
public function loadBaseObject(&$swift)
{
$this->swift =& $swift;
}
}
?>
I then tried mocking it:
Code: Select all
//Unfinished of course!
public function testLoadingPlugin()
{
Mock::generate('FakePlugin');
$plugin_count1 = $this->swift->numPlugins();
$this->swift->loadPlugin(new MockFakePlugin($this));
$plugin_count2 = $this->swift->numPlugins();
$this->assertTrue($plugin_count1 < $plugin_count2);
}
But I get this error when Swift tries to read the pluginName property....
Code: Select all
Exception: testLoadingPlugin -> Unexpected PHP error [Undefined property: MockFakePlugin::$pluginName] severity [E_NOTICE] in [/home/d11wtq/public_html/Swift-2.1.12/Swift.php] line [646]
Exception: testLoadingPlugin -> Unexpected PHP error [Undefined property: MockFakePlugin::$pluginName] severity [E_NOTICE] in [/home/d11wtq/public_html/Swift-2.1.12/Swift.php] line [647]
Exception: testLoadingPlugin -> Unexpected PHP error [Undefined property: MockFakePlugin::$pluginName] severity [E_NOTICE] in [/home/d11wtq/public_html/Swift-2.1.12/Swift.php] line [649]
The mock has deleted the property.... I don't get it

It's a good indication I should have a getter for that purpose but still, where did my property go?

Posted: Thu Aug 17, 2006 6:10 pm
by Chris Corbyn
method_exists() doesn't work on the mock neither.... The whole way the plugins work is based around this method so this basically means it's not possible for me to mock plugins? Am I right?
Posted: Thu Aug 17, 2006 6:11 pm
by Ambush Commander
Hmm... this might mean you'd have to abstract the reflection.
Posted: Thu Aug 17, 2006 6:18 pm
by Chris Corbyn
Ambush Commander wrote:Hmm... this might mean you'd have to abstract the reflection.
Just discussed it with Marcus on SitePoint. The Mocks don't implement properties yet apparently so you need to make setters for all mocks and set them up yourself. Grr... fair enough. he said PHP5 is making it easier though since he has __get() and __set().
The method_exists() solution came to me whilst in discussion.... just extend the mock. I'm not sure what level of control you have over the methods in the extension though (i.e. if you can apply expectOnce() to methods in the subclass). I'll post back.
Posted: Thu Aug 17, 2006 6:51 pm
by Chris Corbyn
Followup: The mock cannot count the number of times methods in a subclass are called. Is it just me or are mocks a little flaky? You can't mock singletons neither, although that's clearly a more fundamental issue.
Posted: Thu Aug 17, 2006 6:55 pm
by Ambush Commander
Realistically speaking, mocks are convenience, an automated way to generate something useful. If the framework proves limiting, you'll have to code it yourself, sometimes not inheriting from the base Mock class at all.
I do this for Singletons: I allow the sole instance to be overridden by the user for testing purposes, where they put in whatever global mock object they need. This is built into the class, so the test harness interfaces with directly and doesn't mock the singleton, in a way, it already is a mock.
Posted: Thu Aug 17, 2006 8:29 pm
by Chris Corbyn
For testing purposes I did this.... and it works. It doesn't feel "too bad".
Code: Select all
<?php
//Look to the subclass MockFakePluginWithEvents for
// the implementation of these methods
class FakePlugin implements Swift_IPlugin
{
public function loadBaseObject(&$swift) {}
public function onLoad() {}
public function onSend() {}
public function onBeforeSend() {}
public function onBeforeCommand() {}
public function onCommand() {}
public function onResponse() {}
public function onUnload() {}
public function onClose() {}
public function onError() {}
public function onFail() {}
public function onLog() {}
public function onConnect() {}
public function onAuthenticate() {}
}
?>
Code: Select all
<?php
//Ignore the calls to parent::<method>()
// ... they are purely to notify the mock
class MockFakePluginWithEvents extends MockFakePlugin
{
public $pluginName = 'Fake';
public $swift;
public function loadBaseObject(&$swift)
{
parent::loadBaseObject(&$swift);
$this->swift =& $swift;
}
public function callSwiftAddCc($address)
{
$this->swift->addCc($address);
}
public function onLoad()
{
parent::onLoad();
}
public function onSend()
{
parent::onSend();
}
public function onBeforeSend()
{
parent::onBeforeSend();
}
public function onBeforeCommand()
{
parent::onBeforeCommand();
}
public function onCommand()
{
parent::onCommand();
}
public function onResponse()
{
parent::onResponse();
}
public function onUnload()
{
parent::onUnload();
}
public function onClose()
{
parent::onClose();
}
public function onError()
{
parent::onError();
}
public function onFail()
{
parent::onFail();
}
public function onLog()
{
parent::onLog();
}
public function onConnect()
{
parent::onConnect();
}
public function onAuthenticate()
{
parent::onAuthenticate();
}
}
?>
And and example of where it was used (or three):
Code: Select all
public function testLoadingAndRemovingPlugin()
{
Mock::generate('FakePlugin');
require_once 'MockFakePluginWithEvents.php';
$plugin = new MockFakePluginWithEvents($this);
$plugin_count1 = $this->swift->numPlugins();
$this->swift->loadPlugin($plugin);
$plugin_count2 = $this->swift->numPlugins();
$this->assertTrue($plugin_count1 < $plugin_count2);
$this->swift->removePlugin('Fake');
$plugin_count3 = $this->swift->numPlugins();
$this->assertTrue($plugin_count3 < $plugin_count2);
}
public function testPluginEventCalls()
{
Mock::generate('FakePlugin');
require_once 'MockFakePluginWithEvents.php';
$plugin = new MockFakePluginWithEvents($this);
$plugin->expectOnce('loadBaseObject', array($this->swift));
$plugin->expectOnce('onLoad');
$plugin->expectOnce('onUnload');
$plugin->expectOnce('onBeforeSend');
$plugin->expectOnce('onSend');
$plugin->expectAtLeastOnce('onBeforeCommand');
$plugin->expectAtLeastOnce('onCommand');
$plugin->expectAtLeastOnce('onResponse');
$plugin->expectAtLeastOnce('onLog');
$this->swift->loadPlugin($plugin);
$this->swift->send('a@b', 'd@c', 'Subj', 'Body');
$this->swift->removePlugin('Fake');
}
public function testPluginAccessToParent()
{
Mock::generate('FakePlugin');
require_once 'MockFakePluginWithEvents.php';
$plugin = new MockFakePluginWithEvents($this);
$this->swift->loadPlugin($plugin);
$cc_count1 = count($this->swift->getCcAddresses());
$this->swift->getPlugin('Fake')->callSwiftAddCc('foo@bar');
$cc_count2 = count($this->swift->getCcAddresses());
$this->assertTrue($cc_count1 < $cc_count2);
$this->swift->removePlugin('Fake');
$this->swift->flushCc();
}