【问题标题】:Accessing database from PHPUnit test从 PHPUnit 测试访问数据库
【发布时间】:2015-03-15 07:49:23
【问题描述】:

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

【问题讨论】:

    标签: php mysql unit-testing mocking phpunit


    【解决方案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)连接到数据库,并在每个测试用例之前清空表(在 setUp 中)。有一个辅助函数可以在表中插入一行;它在需要时调用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-08
      • 2012-03-20
      • 1970-01-01
      • 2020-05-02
      • 2013-08-09
      • 2011-10-17
      • 2018-09-02
      • 2015-09-07
      相关资源
      最近更新 更多