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
)
或者您可以自己制作(取决于您项目的文件结构)。
这样您就不需要 require
或 require_once
来加载您需要测试的 classes
我正在尝试使用 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
)
或者您可以自己制作(取决于您项目的文件结构)。
这样您就不需要 require
或 require_once
来加载您需要测试的 classes