绑定到实现细节的数据库单元测试
Posted
技术标签:
【中文标题】绑定到实现细节的数据库单元测试【英文标题】:Database unit test bound to implementation detail 【发布时间】: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 自己包装起来,不包含任何逻辑并且不必进行测试? 【参考方案1】:不要模拟您不拥有的代码!* 对于使用数据库的类,您必须编写集成测试。好消息是这将迫使您将数据库访问与其他逻辑分开。
*这是来自“Growing object-oriented software,guided by testing”一书的实际建议,由我自己为使用 Doctrine 的实体管理器的代码编写测试的经验提供支持
【讨论】:
以上是关于绑定到实现细节的数据库单元测试的主要内容,如果未能解决你的问题,请参考以下文章
无法绑定到“(ngModel”,因为它不是角度单元测试用例中“输入”的已知属性
如何在单元测试期间覆盖 IQueryable 的 Contains 方法?