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 python的qt5界面
  • ¥15 无线电能传输系统MATLAB仿真问题
  • ¥50 如何用脚本实现输入法的热键设置
  • ¥20 我想使用一些网络协议或者部分协议也行,主要想实现类似于traceroute的一定步长内的路由拓扑功能
  • ¥30 深度学习,前后端连接
  • ¥15 孟德尔随机化结果不一致
  • ¥15 apm2.8飞控罗盘bad health,加速度计校准失败
  • ¥15 求解O-S方程的特征值问题给出边界层布拉休斯平行流的中性曲线
  • ¥15 谁有desed数据集呀
  • ¥20 手写数字识别运行c仿真时,程序报错错误代码sim211-100