我仍然是PHPUnit的新手,我正在试图找出如第3类所示的最佳测试方法。
我理解模拟数据库是如何工作的(我认为),因为它们可以根据输入,XML文件等返回值。我不确定如何为第3个示例提供数据,当SQL查询是在方法本身内部运行。
我正在尝试测试从数据库访问信息并对其执行操作的代码。目前无法将模拟的数据库数据(例如,数组)提供给这些方法。
问题:向内部处理所有SQL查询的方法提供数据的最佳方法是什么?
<?php
class ThisMakesSense {
public function checkPassword($original, $hash) {
return (md5($original) == $hash);
}
}
class ThisMakesSenseTest {
public function testCheckPassword() {
$tms = new ThisMakesSense();
$data = array('jdoe@example.com' => 'password1', 'bsmith@example.com' => 'password2');
foraech ($data as $email => $password) {
$hash = $this->mockDB()->doStuff()->getPasswordHashByEmail($email);
$this->assertTrue($tms->checkPassword($password, $hash), "Password for {$email} is invalid");
}
$tms->checkPassword($password, $hash);
}
}
/* The '$this->_db' object is basically an OOP way of using the
* mysql_* /mysqli_* functions. Replacing it is not an option
* right now.
*/
class DontUnderstand {
public function checkPassword($email, $password) {
$this->_db->query("SELECT password_hash FROM users WHERE email = '{$email}'");
$row = $this->_db->fetchAssoc();
return (md5($password) == $row['password_hash']);
}
}
class DontUnderstandTest extends PHPUnit_Framework_TestCase {
public function testCheckPassword() {
$du = new DontUnderstand();
$data = array('jdoe@example.com' => 'password1', 'bsmith@example.com' => 'password2');
foreach ($data as $email => $pass) {
$this->assertTrue($du->checkPassword($email, $pass), "Password for {$email} is invalid");
}
}
}
(为了省去评论的麻烦,md5和查询方法只是一个简单的例子)
答案 0 :(得分:1)
我不确定什么是最好的方法,但这是我的方式。它基于一个内部与数据库和单个表连接的类的假设。访问是通过INSERT,UPDATE,DELETE,SELECT等类似的,因此没有复杂的JOIN或UNION。另一个假设是我在运行phpunit之前建立了一次数据库(不是测试例程的一部分)。我查看了PHPUnit的数据库扩展,但在我看来这里使用起来太麻烦了,所以我很快就嘲笑了这个:
class UserProfileTest extends PHPUnit_Framework_TestCase {
protected static
$options,
$dbHandle;
public static function setUpBeforeClass() {
self::$options = array(
'dbHostName' => 'localhost',
'dbUserName' => 'tester',
'dbPassword' => 'pw4tester',
'dbName' => 'test',
'dbTableName' => 'Test',
);
self::$dbHandle = new mysqli( self::$options[ 'dbHostName'], self::$options[ 'dbUserName'], self::$options[ 'dbPassword'], self::$options[ 'dbName'] );
if( self::$dbHandle->connect_errno)
exit( 'Error: No DB connection.');
}
protected function fillDb() {
$query = 'INSERT INTO ' . self::$options[ 'dbTableName'] . ' VALUES (default,"foo","bar",7)';
if( !self::$dbHandle->query( $query) )
exit( 'Error: Could not fill DB.');
}
protected function setUp() {
// always start a TC with empty DB
$query = 'DELETE FROM ' . self::$options[ 'dbTableName'];
if( !self::$dbHandle->query( $query) )
exit( 'Error: Could not empty DB.');
}
// -- test --
public function testGetNumberOfProfiles() {
$profileMgr = new UserProfile( self::$options);
$this->assertEquals( 0, $profileMgr->getNumberOfProfiles() );
$this->fillDb();
$this->assertEquals( 1, $profileMgr->getNumberOfProfiles() );
$this->fillDb();
$this->assertEquals( 2, $profileMgr->getNumberOfProfiles() );
}
因此,在实例化类(setUpBeforeClass)时连接到DB,并在每个测试用例之前清空表(在setUp中)。有一个辅助函数可以在表中插入一行;它在需要时被调用。