绑定到实现细节的数据库单元测试

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”,因为它不是角度单元测试用例中“输入”的已知属性

使用 MongoDB 进行单元测试

单元测试 WPF 绑定

如何在单元测试期间覆盖 IQueryable 的 Contains 方法?

android 数据绑定单元测试错误无法解析数据绑定编译器选项。参数:

测试用例cicd怎么实现的