PHPUnit 和定义的变量

PHPUnit and defined variables

我正在尝试使用 PHPUnit 处理一些遗留代码。我的想法是我需要弄清楚将来使用 PHPUnit 对公司来说是否可行或可行。我遇到的一个问题是我们使用大量 define() 函数来设置我们的数据库信息。具体来说,我们使用 'DB_HOST' 来替换我们数据库的名称。我注意到 PHPUnit 不喜欢这种统一所有内容的方法。这是相关代码+错误。

require_once("./includes/class.casestatusprovider.php");
require_once("./config/config_db.php");
require_once("./config/config_tables.php");

class TestCaseStatusProvider extends TestCase
{

    public function setUp()
    {
        $csp = new CaseStatusProvider();
    }

    /**
      * @covers CaseStatusProvider::insert
      */
    public function testInsert_csrIdActive()
    {
        require_once("./config/config_db.php");
        require_once("./config/config_tables.php");
        $this->case_id=10;
        $this->csr_id=1;
        $this->assertNotFalse(insert($this->case_id, $this->csr_id));
    }
}

待测代码

abstract class CaseStatusProvider
 {
    public static function insert($case_id, $csr_id)
    {
        global $DBH;
        global $session;

        if($session->isLoggedIn())
        {
            try{

                $query = "INSERT into ".DB_NAME.".".TBL_CASE_STATUS." (case_id, csr_id, created_by, effective_date, isPast) Values (?, ?, ?, ?, ?) ";
                $data = array($case_id, $csr_id, $session->user_id, time(), 0);
                $STH = $DBH->prepare($query);
                $STH->execute($data);
                $fetched = $STH->fetch();

                return $fetched;

            }catch(PDOException $e) { 
                echo $e->getMessage();
                return false;   
            }
        } 

        return false;           
    }

错误

 Could not connect: Unknown MySQL server host 'DB_HOST' 

那么,我们应该怎么做才能使这项工作成功?顺便说一句,这有点相关,我们也无法弄清楚如何让全局变量为对象正常工作(我们有一个会话对象和一个数据库对象,我不知道为什么 - 我是一名实习生,这是早在我来这里之前就编码了)。

我想我会回答这个问题,因为它更容易。

首先,DI,你现在应该知道这一点(评论)——class 需要像下面这样修改(为了模拟行为)——或者通过使用 setter 但在构造中似乎更好,因为它的状态取决于它们:

abstract class CaseStatusProvider
{

    private $database;

    private $session;

    public funtion __construct($database, $session) {
        $this->database = $database;
        $this->session = $session;
    }

    public static function insert($case_id, $csr_id)
    {
        if($session->isLoggedIn())
        {
            try{

                $query = "INSERT into ".DB_NAME.".".TBL_CASE_STATUS." (case_id, csr_id, created_by, effective_date, isPast) Values (?, ?, ?, ?, ?) ";
                $data = array($case_id, $csr_id, $this->session->user_id, time(), 0);
                $STH = $this->database->prepare($query);
                $STH->execute($data);
                $fetched = $STH->fetch();
                return $fetched;
            }catch(PDOException $e) { 
                echo $e->getMessage();
                return false;   
            }
        } 
        return false;           
    }
}

class classToBeTested extends CaseStatusProvider
{

}

我们的测试用例应该是这样的: 请注意,在使用 DI 时,我们可以强制执行给定 classes.

的行为
class TestCaseStatusProvider extends TestCase
{

    private $session;

    private $database;
    //we need to mock the behavior of the statement in order to retrieve different data sets
    //according to our test cases
    private $pdoStatement;

    private $databaseClass;

    public function setUp()
    {
        //we start by mocking the database
        $this->database = $this->getMock('mysqli'); // I'm guessing mysqli

        //mock the statement in order to controll the fetch method later
        $this->pdoStatement = this->getMock('PDOStatement');
        $this->pdoStatement->method('execute')
            ->willReturn(null); // we'll just mock the fetch method in our test cases

        $this->database->method('prepare')
            ->willReturn($pdoStatement); // we mock the retrieval of a PDOStatement

        //then we mock the session
        $this->session = $this->getMock('YourSessionClass');
        //since you are accessing user_id from the class you should mock it
        $this->session->user_id = 20;

        $this->databaseClass = new classToBeTested( $this->session);
    }


    public function testInsertOk()
    {
        //We mock that the user is logged in
        $this->session->method('isLoggedIn')
             ->willReturn(true);
        $this->pdoStatement->method('fetch')
            ->willReturn(array()); // we'll just mock the fetch method, no actual data needed here
        $this->assertNotFalse($this->databaseClass->insert(1, 1));
    }

    public function testInsertKo1()
    {
        //We mock that the user is logged in
        $this->session->method('isLoggedIn')
             ->willReturn(false);
        //no need to mock the fetch method because it will not be executed
        $this->assertFalse($this->databaseClass->insert(1, 1));
    }

    public function testInsertKo2()
    {
        //We mock that the user is logged in
        $this->session->method('isLoggedIn')
             ->willReturn(true);
        $this->pdoStatement->method('fetch')
            ->will($this->throwException(new PDOException)); //mock exception on insert
        $this->assertFalse($this->databaseClass->insert(1, 1));
    }
}

P.S : 尝试改变你的 classes 以采用 [单一责任原则1

简而言之插入方法应该只插入(不检查用户是否登录 - 这应该在另一个 class 中完成,它有一个 CaseStatusProvider 的实例检查用户是否登录)和 return 如果错误(或抛出异常)则为 true 或 false

P.S.S : 提供的代码可能有错别字,我没有 运行 它...

配置(link):

<phpunit
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.5/phpunit.xsd"

         <!--bootstrap="/path/to/bootstrap.php"--> 
         [...]
         >
  <!-- ... -->
</phpunit>

您可以传递一个 bootstrap 文件以访问您项目中的所有 classes(如果您使用的是构建框架(例如:Symfony makes bootstrap.cache.php

或者您可以自己制作(取决于您项目的文件结构)。

这样您就不需要 requirerequire_once 来加载您需要测试的 classes