Я пытаюсь работать с 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, вы должны знать это сейчас (комментарии) — класс нужно изменить, как показано ниже (для того, чтобы смоделировать поведение) — или с помощью сеттеров, но в конструкторе кажется лучше, потому что его состояние зависит от них:
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 мы можем форсировать поведение данных классов.
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 : Попробуйте изменить свои классы, чтобы принять принцип единой ответственности1
Короче метод вставки должен просто вставить (не проверять, зарегистрирован ли пользователь — это должно быть сделано в другом классе, который имеет экземпляр CaseStatusProvider, который проверяет, вошел ли пользователь в систему) и возвращает true или false, если ошибка (или выбросить исключение)
P.S.S : Предоставленный код может иметь опечатки, я не запускал его …
<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>
Вы можете передать файл начальной загрузки, чтобы иметь доступ ко всем классам в вашем проекте (если вы используете фреймворк, он собирается (например, Symfony делает bootstrap.cache.php
)
Или вы можете сделать свой собственный (зависит от структуры файлов ваших проектов).
Этот способ вам не нужен require
или же require_once
загрузить классы, которые нужно протестировать
Других решений пока нет …