dpwtr666638 2016-07-08 21:19
浏览 41
已采纳

PHPUnit和定义的变量

I am trying to work with PHPUnit on some legacy code. The idea is I need to find out if using PHPUnit in the future is viable or feasible for the company. One problem I'm having is that we use a lot of define() functions to set up our database information. Specifically, we use 'DB_HOST' to substitute over our database's name. I'm noticing that PHPUnit isn't liking this method of getting everything uniform. Here is the relevant code + error.

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));
    }
}

CODE TO BE TESTED

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;           
    }

ERROR

 Could not connect: Unknown MySQL server host 'DB_HOST' 

So, what should we be doing to make this work? As an aside that's slightly relevant, we also can't figure out how to get global variables to work properly for objects (we have a session object and a database object, I don't know why - I'm an intern and this was coded long before I was here).

  • 写回答

1条回答 默认 最新

  • dongnei3634 2016-07-11 16:15
    关注

    I thought I would answer this because it's easier.

    First of all DI, you should know this by now (comments) - the class need to be modified like below (in order to mock behavior) - or by using setters but in construct seems better because it's state depends on them:

    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
    {
    
    }
    

    And our test case should look something like this: Please note that when using DI we can force the bevavior of the given 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 : Try to change your classes to adopt the [single responsibility principle1

    In short the insert method should just insert (not check if the user is logged - this should be done in another class which has an instance of CaseStatusProvider which checks is the user is logged) and return true or false if error (or throw exception)

    P.S.S : The provided code might have typos, I haven't ran it...

    Configuration (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>
    

    You can pass a bootstrap file to have access to all classes in your project (if you are using a framework it is build (ex: Symfony makes bootstrap.cache.php)

    Or you can make your own (depends on your projects's file structure).

    This way you do not need require or require_once to load classes which you need to test

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 保护模式-系统加载-段寄存器