Can you use $this inside callback to get protected properties of mocked class in phpunit? Or is there other way to achieve that?
$mock = $this->getMock('A', array('foo'));
$mock->expects($this->any())->method('foo')->will(
$this->returnCallback(function() {
return $this->bar;
}));
This could be really useful if you think about injecting mocked objects. Sometimes class has hard-coded dependency for other class, but it creates it with method which you could theoretically mock and create mocked object instead of hard-coded object. Please look on the other example.
class A {
protected $bar = "bar";
public function foo () {
$b = new B();
return $b->fizz($this->bar);
}
}
class B {
public function fizz ($buzz) {
return $buzz;
}
}
But lets say class B does something bad and I would like to replace it with mock.
$mockB = $this->getMock('B');
// (...) - and probably mock other things
$mockA = $this->getMock('A', array('foo'));
$mockA->expects($this->any())->method('foo')->will(
$this->returnCallback(function() use ($mockB) {
return $mockB->fizz($this->bar);
}));
Is this somehow achievable?
Of course without any surprise, currently, if I do this like above then I get error:
PHP Fatal error: Using $this when not in object context in (...)
Using use
keyword I can inherit $mockA from parent scope:
$mockB = $this->getMock('B');
// (...) - and probably mock other things
$mockA = $this->getMock('A', array('foo'));
$mockA->expects($this->any())->method('foo')->will(
$this->returnCallback(function() use ($mockA, $mockB) {
return $mockB->fizz($mockA->bar);
}));
but this way I will try to access bar as public and I will get:
PHP Fatal error: Cannot access protected property (...)
As other answers have pointed out, $this
can be used in Closures since PHP 5.4. A lesser known fact is that you can bind a closure to arbitrary objects and in fact make their private properties accessible like that. The method you need is bindTo()
, which returns a new closure with different context.
$cb = function() {
return $this->bar;
};
$cb = $cb->bindTo($mockA);
Or more precise, your example would look like that:
$mockB = $this->getMock('B');
// (...) - and probably mock other things
$mockA = $this->getMock('A', array('foo'));
$fooCallback = function() use (&$mockB) {
return $mockB->fizz($this->bar);
};
$mockA->expects($this->any())->method('foo')->will(
$this->returnCallback($fooCallback->bindTo($mockA)));
As pointed out by dev-null-dweller, in PHP 5.4 you can use $this within the closure as if you where working normally in the method.
In 5.3 you can mimic this behavior by doing:
public function getCallback(B $b) {
$self = $this;
return function() use($b, $self) {
return $b->fizz($self->bar);
};
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With