实例模拟和隐式构造函数

时间:2013-08-10 10:29:03

标签: unit-testing tdd phpunit mockery

我正在尝试在管理数据库连接的类上使用TDD。但是

  • 我经常远离数据库可用的网络
  • 我想测试那些没有真正连接的类,甚至是SQLite :memory:
  • 我可能希望以独立于平台的方式测试连接(例如,为MySQLi对象交换PDO对象等)。特别是因为数据库不是所有的MySQL,有些是SQLServer。

基本上我想这样做:

class ConnectionManager {
    ...
    public function getConnection($name) {
        $params = $this->lookup($name);
        return new \PDO($params['spec'], $params['username'], $params['password']);
    }
}

在我的测试跑步者中:

class ConnectionManagerTest extends \PHPUnit_Framework_TestCase {
    public function testGetConnection() {
        $cxn = new ConnectionManager(); 
        $this->assertNotNull($cxn->getConnection('test')); // or whatever
    }
}

不知怎的,我想使用PDO类的模拟。我唯一的选择是向测试类构造函数或其方法之一添加显式参数吗?我尝试使用Mockery文档中的'Instance mocking',但是因为我使用自动加载会导致'致命错误无法重新声明类'(duh)。

我不想用纯粹用于测试的代码来污染合同,但这是我唯一的选择吗?

感谢您的帮助

1 个答案:

答案 0 :(得分:3)

这看起来像依赖注入可以帮助您的情况。网上有很多很好的文章描述了这种技术(这里是wikipedia overview),而前面的stack overflow answer给出了该技术的简明解释(例子是Java中的,但它应该解释模式并且可以很容易地转换成另一种语言。)

关于“不想用纯粹用于测试的合同污染代码”的观点是一个有趣的观点。我认为,随着您获得TDD的更多经验,您会发现,编写易于测试的代码,编写良好的干净代码,实质上是同一枚硬币的两面。 TDD的一个微妙的力量(正确实践)是它引导您走向一个良好,干净的设计,可以很容易地与您的应用程序一起成长。通过遵循将测试放在您设计代码的核心的方法,您实际上是从客户端的角度设计代码,这样可以更清晰地接口代码中的不同组件,并最终使使用这些接口的代码更加清晰。

依赖注入是遵循TDD时应用的常用技术,并且是这些要点的一个很好的例证。一方面它对测试有效,因为它允许您在测试时轻松交换组件,这样您就可以避免必须调用数据库,模拟和验证组件之间的交互等等。但是,它还支持通常良好的设计,因为它会引导您实现低耦合和灵活的设计(例如,如果您是依赖注入数据库的访问权限,那么将数据源从数据库更改为其他数据非常简单技术)。