实例模拟和隐式构造函数
Posted
技术标签:
【中文标题】实例模拟和隐式构造函数【英文标题】:Instance mocking and implicit constructors 【发布时间】:2013-08-12 05:31:48 【问题描述】:我正在尝试在管理数据库连接的类上使用 TDD。不过
我经常远离可用数据库的网络进行开发 我只想测试类,不要弄乱真正的连接,即使是 SQLite:memory:
我可能想以独立于平台的方式测试连接(例如,将 PDO 对象交换为 mysqli 对象等)。尤其是数据库不全是 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 文档使用“实例模拟”,但由于我使用自动加载导致“致命错误无法重新声明类”(duh)。
我不希望使用纯粹用于测试的代码来污染合约,但这是我唯一的选择吗?
感谢您的帮助
【问题讨论】:
你的连接管理器总是(并且只)返回一个PDO
。所以你可能首先需要一个不同的连接管理器,因为你的测试现在已经出来了?
实际上连接管理器的问题不太重要,因为 PDO 支持多种不同的连接类型。主要问题是在网络外开发时,真正的连接不可用。稍后我可能不得不将这些“普通的旧 PHP 对象”放入框架中,但我还不知道是哪个,所以我希望尽可能与实现无关
好吧,我只是因为名字而戳了戳。如果您需要该工作,请创建class ConnectionManagerStub extends ConnectionManager
,然后更改您需要测试的那些,以便在保留存根以进行协作的同时消除依赖关系,例如对于您的测试根本不使用任何 PDO(或内存中的一个或其他什么)。
【参考方案1】:
这看起来像是 依赖注入 可以帮助您的情况。网上有很多描述这种技术的好文章(这里是wikipedia overview),之前的stack overflow answer 给出了该技术的简明解释(示例是Java,但它应该解释模式并且可以转移到另一种语言很容易)。
您关于“不想用纯粹用于测试的合约污染代码”的观点很有趣。我想你会发现,随着你对 TDD 有更多的经验,编写易于测试的代码和编写好的干净代码本质上是同一枚硬币的两个方面。 TDD 的一个微妙的力量(如果实践得当)是它引导你走向一个好的、干净的设计,可以很容易地随着你的应用程序增长。通过遵循将测试置于代码设计核心的方法,您实际上是从客户的角度设计代码,这会导致代码中不同组件的接口更清晰,并最终使使用这些接口的代码更清晰。
依赖注入是遵循 TDD 时常用的技术,并且很好地说明了这些要点。一方面它对测试很有效,因为它允许您在测试时轻松交换组件,这样您就可以避免调用数据库、模拟和验证组件之间的交互等。但是,它也支持一般良好的设计,因为它引导您走向低耦合和灵活的设计(例如,如果您对数据库进行依赖注入访问,那么将数据源从数据库更改为其他数据库变得相当简单技术)。
【讨论】:
DI 显然是一种可能性,但在 PHP 中实现起来很棘手。通常,您需要一个带有 IoC 容器或诸如 Symfony2 或 Laravel 之类的框架。我希望在赛道上使用 Laravel,但目前无法考虑。因此,我尽可能多地使用“POPO”(普通旧 PHP 对象),并在获取容器和框架之前很久就证明该应用程序可以使用 TDD。 也许应该补充一点,您关于更改数据源技术的最后评论也是相关的,因为我正在使用的平台包含一个 SaaS 组件,其中一些事务(尤其是写入)只能通过 REST 服务获得。使代码尽可能与数据源无关的另一个原因。 啊有趣 - 没有意识到语言会如此不同。 W.R.T 必须使用 IoC 框架 - 您可能可以在 PHP 中以不同方式模拟 DI。例如,您可以简单地将 PDO 对象分配给被测类中的变量,然后使用Setter Injection 注入模拟版本进行测试(或者只是将模拟对象分配给变量)以上是关于实例模拟和隐式构造函数的主要内容,如果未能解决你的问题,请参考以下文章