Mocking an object - getting a strange error

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")

Moderator: General Moderators

User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Post by Weirdan »

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)
You mean something like this? :

Code: Select all

$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?
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

The definition would be more like:

Code: Select all

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.
Post Reply