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

时间:2014-09-19 09:26:58

标签: php unit-testing dependency-injection zend-framework2 phpunit

我有一个简单的PHP-Class,它包含对数据库的访问以检索用户并希望对其进行单元测试。我目前有以下代码:

要测试的课程:

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)的实现细节。

防止单元测试依赖于实现细节的最佳做法是什么?是否值得努力设置单元测试可以运行的实际测试数据库(这也会大大减慢测试的执行速度),还是有更好的方法来模拟表网关?

1 个答案:

答案 0 :(得分:2)

不要模拟您不拥有的代码!*对于使用数据库的类,您必须编写集成测试。好处是,这将迫使您将数据库访问与其他逻辑分开。

*这是来自"增长的面向对象软件的实际建议,由测试指导"本书以我自己为使用Doctrine实体经理的代码编写测试的经验作为后盾