Reputation: 83
I'm having some problems understanding mock objects.
What I want is an observer that works normally but do make sure the methods are called with the right parameters.
from what I understand so far this should be what I'm looking for: Observer:
class Observer
{
public function returnFooIfBar($bar)
{
return ($bar == 'bar') ? 'foo' : ;
}
}
Subject:
class Subject
{
$obs;
__construct(Observer $dependency)
{
$this->obs = $dependency;
}
public function tradeStrings($string)
{
$this->obs->returnFooIfBar($string);
}
}
Test:
class SubjectTest
{
public function testCallsObsMethod()
{
$obs = $this->getMock('Observer') ;
$obs->expect($this->once())
->method('returnFooIfBar')
->with($this->equlTo('bar')) ;
$subj = new Subject($obs);
$returnString= $subj->TradeStrings('bar') ;
$this->assertEqual('foo', $returnString) ;
}
}
From what I understand this tests that:
As I understand no functionality of the original class is changed, other than the constructor / autoloading not being run.
If I mock a method when running getMock() the mocked object's method will only return something if I specify it.
$obs = $this->getMock('Observer', array('returnFooIfBar'));
$obs->expects($this->once())
->method('returnFooIfBar')
->with('bar')
->will($this->returnValue('foo');
Do I understand this right? if not could you please clarify for me as I would love some clarity on this. :)
Edit: Changed the post to make it clearer what I am after and how I currently understand it.
Upvotes: 3
Views: 2902
Reputation: 441
You can do this now in phpunit with enableProxyingToOriginalMethods()
.
class SubjectTest extends \PHPUnit\Framework\TestCase
{
public function testCallsObsMethod()
{
$obs = $this->getMockBuilder('Observer')
->enableProxyingToOriginalMethods()
->getMock();
$obs->expect($this->once())
->method('returnFooIfBar');
$subj = new Subject($obs);
$returnString= $subj->tradeStrings('bar') ;
$this->assertEquals('foo', $returnString) ;
}
}
Upvotes: 2
Reputation: 1528
If you let phpunit create a mocked object, it internally builds up a new temporary class which extends the original one and implements all methods of this class with mock specific code.
The idea behind this is to decouple objects in your test cases. while your given example is valid, you would not use it this way.
but your example test would fail anyway, because the mocked function returnStringFooIfBar
would not return anything.
this is how it should work:
$obs = $this->getMock('observer') ;
$obs->expect($this->once())
->method('returnStringFooIfBar')
->with($this->equlTo('bar'))
->will($this->returnValue('foo'));
$returnString= $obs->returnStringFooIfBar ('bar') ;
$this->assertEqual('foo', $returnString) ;
but a real world test case would involve some object to test:
class TestObject {
private $observer;
public function __construct($observer) {
$this->observer = $observer;
}
public function doMagicAndNotify() {
// do heavy magic
//notify observer
$obsresult = $this->observer->returnStringFooIfBar('bar');
return 'didmagic';
}
}
class TestObjectTest extends PHPUnit_Framework_TestCase {
public function testObserverCalling() {
$obs = $this->getMock('observer') ;
$obs->expect($this->once())
->method('returnStringFooIfBar')
->with($this->equlTo('bar'));
$test = new TestObject($obs);
$returnString= $test->doMagicAndNotify() ;
$this->assertEqual('didmagic', $returnString) ;
}
}
EDIT:
What I want is an observer that works normally but do make sure the methods are called with the right parameters.
As I understand no functionality of the original class is changed, other than the constructor / autoloading not being run.
It is actually the other way around. The temporary child class of Observer overwrites all (or specified) methods and changes the original functionality (the parent of mocked methods is simply not executed). it does not overwrite the constructor, its called anyway.
It is not possible to assert a method call of a mocked method and call its original method at the same sime.
See Method Template and the Generator for reference.
And please remember: you are not testing the correct behaviour of your observer here, your are mocking its beheviour in order to test the subject.
sitenote: $this->returnCallback($someCallback)
is a mighty function and might help you.
i don't like the idea, but you could do something like this:
public function testObserverCalling() {
$obs = new Observer();
$obsmock = $this->getMock('observer') ;
$obsmock->expect($this->once())
->method('returnStringFooIfBar')
->with($this->equlTo('bar'))
->will($this->returnCallback(array($obs, 'returnStringFooIfBar')));
$test = new TestObject($obsmock);
$returnString= $test->doMagicAndNotify() ;
$this->assertEqual('didmagic', $returnString) ;
}
Upvotes: 7