Мне бы хотелось иметь PHPUnit Mock, который выполняет метод, как обычно, но затем каким-то образом изменяет возвращаемое значение, прежде чем функция вернется.
Что у меня есть
У меня есть набор производных классов, аналогичных приведенным ниже:
abstract class Base
{
abstract protected function getUrl();
public function callUrl() {
$url = $this->getUrl();
// some code to call the URL here
}
}
class Foo extends Base
{
protected function getUrl() {
return "http://www.example.com/Foo";
}
}
class Bar extends Base
{
protected function getUrl() {
return "http://www.example.com/Bar";
}
}
Обратите внимание, что у меня классы намного сложнее, и некоторые из предметов, которые я должен проверить, имеют побочные эффекты (например, запись в базу данных и т. Д.).
Наивный подход с дублированием кода
Если бы у меня был только один производный класс (например, Foo), то я мог бы сделать следующее:
class FooMock extends Foo
{
protected function getUrl() {
return parent::getUrl() . "?sandbox";
}
}
class theTest extends PHPUnit_Framework_TestCase
{
public function testIt() {
$mock = new FooMock();
// assert something
}
}
К сожалению, это означает, что мне понадобится определенный класс "Mock" для каждого производного класса, который я хочу протестировать, и все они выполняют одну и ту же функцию.
Предпочтительный подход
Вместо этого я хотел бы иметь возможность сделать что-то вроде следующего:
function callback ($returnValue) {
return $returnValue . "?sandbox";
}
class theTest extends PHPUnit_Framework_TestCase
{
private $mock;
public function testFoo() {
$this->mock = $this->getMockBuilder('Foo')->getMock();
$this->setupMock();
// assert something
}
public function testBar() {
$this->mock = $this->getMockBuilder('Bar')->getMock();
$this->setupMock();
// assert something
}
public function setupMock() {
$this->mock->expects($this->any())
->method('getUrl')
->will($this->postProcessReturnValue('callback'));
}
}
Это вообще возможно с PHPUnit?
Обновление : Было предложено, чтобы у меня был экземпляр исходного класса и экземпляр ложного класса. Используйте оригинальный класс, чтобы получить исходное возвращаемое значение и изменить его. Это измененное значение затем используется в качестве возврата для макета. Это неосуществимый способ, так как классы более сложные (у них есть побочные эффекты, такие как запись в БД).
Пример, где это не сработает;
class Foo extends Base
{
$id = 0;
public function saveToDB() {
$this->id = saveToDBAndReturnId();
}
protected function getUrl() {
if ($this->id > 0) {
return "http://www.example.com/".$this->id;
}
throw new Exception("No ID");
}
}
$foo = new Foo();
$foo->saveToDB();
$url = $foo->getUrl();
Очевидно, что возвращаемый URL будет отличаться для нескольких вызовов. Я всегда мог высмеивать saveToDB
, но это начинает казаться грязным, когда все, что я хочу сделать, это постобработать результат getUrl
.