我必须说我不确定我是否同意如果不在这里访问数据库,单元测试会毫无意义。我的目标是在不涉及您的数据库的情况下获得生成被测 SQL 的业务逻辑。这是我正在谈论的一个例子:
class Foo {
// ... Getters and setters for your config ...
public function doSomeBusinessLogicThenHitDb()
{
$sql = 'SELECT * FROM mytable WHERE ';
$sql .= $this->_doSomethingComplicatedThatInvolvesParsingTheConfig();
$this->_queryDb($sql);
}
protected function _queryDb($sql)
{
// Do something with a PDO or whatever
}
}
将该_queryDb()
位抽象为一个单独的函数后,您可以编写以下测试:
public function testMyClassUnderSomeCircumstances()
{
// Set up config
$exampleConfig = // whatever
// Set up expected result
$whatTheSqlShouldLookLikeForThisConfig = 'SELECT ... WHERE ...';
// Set up a partial mock that doesn't actually hit the DB
$myPartialMockObject = $this->getMock('Foo', array('_queryDb'), array(), '');
$myPartialMockObject->expects($this->once())
->method('_queryDb')
->with($whatTheSqlShouldLookLikeForThisConfig);
// Exercise the class under test
$myPartialMockObject->setConfig($exampleConfig);
$myPartialMockObject->doSomeBusinessLogicThenHitTheDb();
}
该测试的重点是测试生成SQL 的业务逻辑,而不是测试数据库本身。通过期望生成的 SQL 必须看起来像它必须看起来的样子,你可以确保如果无辜的重构_doSomethingComplicatedThatInvolvesParsingTheConfig()
意外地破坏了你的代码,你的测试将会失败,因为它会产生与过去不同的 SQL。
如果测试整个应用程序(包括其数据库)是您的目标,请尝试使用适当的集成测试套件,例如 Selenium。单元测试监视各个类并告诉您它们何时停止按应有的行为。如果您让它们超出范围,您将面临执行速度和错误本地化的问题(即错误甚至在代码中,更不用说被测试的类,还是数据库的东西?)。