Discussion of testing theory and practice, including methodologies (such as TDD, BDD, DDD, Agile, XP) and software - anything to do with testing goes here. (Formerly "The Testing Side of Development")
To avoid any chance of method name collisions, I'd use a "proxy" object which takes the mock object as a constructor parameter, and has nothing else declared except __call(), __set() and __get(). I then pass this object as the dependency/mock. (This is what I use in Smalltalk, paraphrased in PHP)
$mock = new Mock('ClassName');
$expectation = $mock->expectCall('someMethod');
$expectation->acceptArgs($arrayOfArgs);
$expectation->expectCallCount(2);
class Proxy {
private $_obj;
public function __construct($obj) {
$this->_obj = $obj;
}
public function __call($method, $args) {
return call_user_func_array(array($this->_obj, $method), $args);
}
public function __set($name, $value) {
$this->_obj->$name = $value;
}
public function __get($name) {
return $this->_obj->$name;
}
}
$anotherObj = new AnotherClass(new Proxy($mock));
$anotherObj->doWhatever();
I don't see how this would prevent any name clashes... suppose the ClassName has expectCall method of its own... how would you go about preventing name clashes in that case?
class MockProxy {
private $_mock;
public function __construct ($mock) {
$this->_mock = $mock;
}
public function __call($method, $args) {
return $this->_mock->returnForCall($method, $args);
}
public function __get($propety) {
return $this->_mock->returnPropety($property);
}
public function __set($property, $val);
$this->_mock->setProperty($property, $val);
}
}
$mock = new MockObject('SomeClass');
$expect = $mock->expectCall('someMethod');
$expect->expectCallCount(1)->with(true, 1)->with(false, 2);
$objectBeingTested = new ClassBeingTested($mock->proxy());
// within test..
$this->assertEqual($objectBeingTest->whatever(), 'foo');
The Proxy handles the interface mocking, whilst the MockObject handles the behaviour mocking, and the expectation handles the expected behaviour of a method or property.
The Proxy also allows for "tidier" testing. The Poxy passes anything and everything passed to it, and the MockObject will store any invalid or unexpected calls until such a time when you validate, so the test doesn't break with unexpected failed calls and allows the test/spect suite to continue.