【发布时间】:2014-11-13 20:30:06
【问题描述】:
我有一个简单的 PHP 类,它封装了对数据库的访问以检索用户并希望对其进行单元测试。我目前有以下代码:
要测试的类:
class UserTable {
protected $tableGateway;
public function __construct(\Zend\Db\TableGateway\TableGateway $tableGateway) {
$this->tableGateway = $tableGateway;
}
public function getUserWithId($id) {
return $this->tableGateway->select(['id' => $id])->current();
}
}
单元测试:
class UserTableTest extends \PHPUnit_Framework_TestCase {
public function testGetUserWithIdReturnsCorrectUser() {
$user = new User();
$resultSet = new ResultSet();
$resultSet->initialize([$user]);
$mockTableGateway = $this->getMock('\Zend\Db\TableGateway\TableGateway', ['select'], [], '', false);
$mockTableGateway->expects($this->once())->method('select')->with(['id' => 1])->willReturn($resultSet);
$userTable = new UserTable($mockTableGateway);
$this->assertEquals($user, $userTable->getUserWithId(1));
}
}
但是,如果我后来决定更改使用表网关的方式(例如使用 select(['id = ?' => $id]),现在单元测试将失败。这会将单元测试绑定到应该避免的getUserWithId($id) 的实现细节。
防止单元测试依赖于实现细节的最佳做法是什么?是否值得努力建立一个可以运行单元测试的实际测试数据库(这也会大大减慢测试的执行速度)还是有更好的方法来模拟表网关?
【问题讨论】:
-
您可以创建自己的网关对象,您始终使用该对象,然后这些表测试仅与一个网关接口,但如果实现更改,您只需在自定义网关中更改它,而不是在使用的任何地方那个 zend 表网关
-
这就是我的 UsersTable 的实际设计目的。进一步封装 Zend TableGateway 并为应用程序的其余部分提供接口。那么,您建议添加另一个抽象级别,它只是将 TableGateway 自己包装起来,不包含任何逻辑并且不必进行测试?
标签: php unit-testing dependency-injection zend-framework2 phpunit