抽象方法没有被嘲笑
Posted
技术标签:
【中文标题】抽象方法没有被嘲笑【英文标题】:Abstract method not getting mocked 【发布时间】:2020-07-21 01:35:38 【问题描述】:我有一个类似于这个简化示例的设置(应该通过直接复制粘贴来工作),但是我想模拟 Controller->setModel()
的方法没有被模拟。也没有出现错误:
<?php
use PHPUnit\Framework\TestCase;
// Implementation
class Model
// The constructor here would receive a PDO, but that's not important
public $data = [
['id' => 1, 'text' => 'nope'],
['id' => 2, 'text' => 'no'],
['id' => 3, 'text' => 'not']
];
public function getItem($id)
// And here we would query with the PDO instead, but again
// the question is more about mocking the setModel() method
return $this->data[$id];
abstract class Endpoint
public function __construct()
echo "\n1. Calling abstract class constructor\n";
$this->model = $this->setModel();
abstract protected function setModel();
class Controller extends Endpoint
public function getStuff($id)
echo "3. Getting stuff from controller\n";
$data = $this->model->getItem($id);
return ['id' => $data['id'], 'text' => $data['text']];
protected function setModel()
echo "2. Setting ACTUAL model of controller\n";
$config = 'sqlite:myfile.sqlite3'; // Suppose this file exists and is valid
$pdo = new \PDO($config);
return new Model($pdo);
// Test
final class ControllerTest extends TestCase
public function testExample()
$mockModel = \Mockery::mock('FakeModel')
->shouldReceive('getItem')
->andReturn(['id' => 123, 'text' => 'myCustomText']);
$mock = \Mockery::mock('Controller')->makePartial()
->shouldAllowMockingProtectedMethods();
$mock->shouldReceive('setModel')->andReturn($mockModel);
$controller = new Controller();
$result = $controller->getStuff(2);
$this->assertEquals('myCustomText', $result['text']);
测试结果如下。 PDOException 是因为 php5-sqlite 驱动程序不再适用于我的本地 VM(Ubuntu 18.04)。我正在使用 Docker 映像进行设置,但同样的模拟问题也发生在其中。但这不是重点——重点是模拟setModel()
:
1. Calling abstract class constructor
2. Setting ACTUAL model of controller
Time: 110 ms, Memory: 7.25MB
There was 1 error:
1) ControllerTest::testExample
PDOException: could not find driver
/home/juha/koodaus/pastes/backend/test/Foo/FooTest.php:45
/home/juha/koodaus/pastes/backend/test/Foo/FooTest.php:26
/home/juha/koodaus/pastes/backend/test/Foo/FooTest.php:63
ERRORS!
Tests: 1, Assertions: 0, Errors: 1.
正如您从第二次调试打印中看到的 - Setting ACTUAL model of controller
- 完全忽略了模拟。也不会出现错误或警告。模拟设置有什么问题?
我也尝试过使用overload
和alias
,但它们不起作用,即使使用:
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
导致:
Mockery\Exception\RuntimeException: Could not load mock Controller, class already exists
但我认为这是一个已知问题。
版本(不支持 PHP 7 的旧版设置):
PHP 5.6.40 PHPUnit 5.7.27 嘲讽1.3.1
【问题讨论】:
【参考方案1】:我认为您无法按照自己的方式设置模拟。当您调用 ->makePartial() 或 ->make 或 ->getMockForAbstractClass() 时,将调用构造方法。所以 $this->setModel();总是返回 null。
我会建议这个解决方案。
public function testExample()
$mock = $this->getMockBuilder(Controller::class)
->setMethods(['setModel'])->getMock();
$mockModel = \Mockery::mock('FakeModel');
$mockModel->shouldReceive('getItem')
->andReturn(['id' => 123, 'text' => 'myCustomText']);
$mock->method('setModel')->willReturn($mockModel);
$mock->instuctModel();
$result = $mock->getStuff(2);
$this->assertEquals('myCustomText', $result['text']);
而endPoint Abstract 类应该改成下面这个
abstract class Endpoint
protected $model;
public function __construct()
echo "\n1. Calling abstract class constructor\n";
public function instuctModel()
$this->model = $this->setModel();
abstract public function setModel();
【讨论】:
以上是关于抽象方法没有被嘲笑的主要内容,如果未能解决你的问题,请参考以下文章