从 PHPUnit 测试访问数据库


Accessing database from PHPUnit test

我对 PHPUnit 还很陌生,我正在尝试弄清楚如何最好地测试第 3 类中所示的方法。

我了解模拟数据库的工作原理(我认为),因为它们可以根据输入、XML 文件等返回值。当 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 和查询方法只是一个简单的例子)

我不确定什么是最好的方法,但这是我的方法。它基于在内部与数据库和单个表连接的类的假设。访问是通过插入、更新、删除、选择和类似方式进行的,因此没有复杂的联接或联合。另一个假设是,在运行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),并在每个测试用例之前清空表(在setUp中)。有一个辅助函数,可以在表中插入一行;在需要时调用它。