php - 从 PHPUnit 测试访问数据库

标签 php mysql unit-testing mocking phpunit

我对 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和查询方法只是简单的例子)

最佳答案

我不确定什么是最好的方法,但这是我的方法。它基于在内部连接数据库和单个表的类的假设。通过 INSERT、UPDATE、DELETE、SELECT 和类似方式进行访问,因此没有复杂的 JOIN 或 UNION。另一个假设是我在运行 phpunit 之前建立了一次数据库(不是作为测试例程的一部分)。我查看了 PHPUnit 的数据库扩展,但在我看来它在这里使用起来太麻烦了,所以我很快 mock 了这个:

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 中)。有一个辅助函数可以在表中插入一行;它在需要时被调用。

关于php - 从 PHPUnit 测试访问数据库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27988501/

相关文章:

php - 使用 php 剪切 flv 视频

php - 查找多个间隔之间的最长间隔

php - 如何使用 Volley 向 Mysql 数据库插入数据

java - 伪造没有接口(interface)的类

java - 模拟对象来运行不同的 void 方法

php - 终极密码盐

mysql - 从数据库中获取交叉记录

mysql - 显示变量 mysql - 我在哪里可以找到这些参数的文件

python - Django 单元测试数据库没有被拆除?

php - 无法将查询结果用作变量