Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

PHP5_nachinayushim

.pdf
Скачиваний:
29
Добавлен:
20.03.2015
Размер:
26.79 Mб
Скачать

Учебный пример: диспетчер протоколирования на PHP 673

<?php

require_once ("../settings-test.php"); require_once ("class.UserLog.php"); require_once ("phpunit/phpunit.php");

class TestUserLog extends TestCase { private $ulGood = null;

private $ulBad = null;

Затем определяется специфический контекст, в котором будут выполняться тесты. Это делается путем определения значений в PHPUnit+функции setUp(). В данном случае создается два UserLog+объекта:

function setUp()

{

$this->ulGood = new UserLog (

array ('site'

=> 1,

'section'

=> 2,

'login'

=> "3",

'session'

=> "1E23553",

'firstname'

=> "Alice",

'lastname'

=> "AppleGate",

'address1'

=> "123Main",

'city'

=> "Sandusky",

'state'

=> "OH",

'zip'

=> "44870",

'demo0'

=> "21",

'demo1'

=> "15",

'demo2'

=> "22",

'demo3'

=> "26"));

$this->ulBad = new UserLog (

array ('site'

=> 0,

'section'

=> 0,

'login'

=> "",

'session'

=> "1E23553",

'firstname'

=> "Alice",

'lastname'

=> "AppleGate",

'address1'

=> "123Main",

'city'

=> "Sandusky",

'state'

=> "OH",

'zip'

=> "44870",

'demo0'

=> "21",

'demo1'

=> "15",

'demo2'

=> "22",

'demo3'

=> "26"));

}

 

Необходимо определить тестовые функции.

Первая функция, testValid0(), использует PHPUnit+функцию assertEquals() для проверки следующих условий:

Sandusky ++++++ город, представленный в UserLog+объекте $ulGood; $ulGood ++++++ корректный журнал;

$ulGood не имеет недостоверных данных.

Если тесты выполняются, то контекст, в котором они работают (по крайней мере, для объекта $ulGood), работает корректно. Чтобы изучить код функции assertEquals(), необходимо просмотреть файл phpunit.php из инсталляции PHPUnit:

674 Глава 17

function testValid0()

{

$this->assertEquals("Sandusky", $this->ulGood->city, "invalid city"); $this->assertEquals(true, $this->ulGood->isValid(), "valid log"); $this->assertEquals(0, count ($this->ulGood->getInvalidData()),

"valid count");

}

В следующем тесте создается новый UserLog+объект, $ul, и заполняется некото+ рыми некорректными данными (а именно значением site). Формулируется утвержде+ ние, что функция isValid() вернет значение false, и что количество некорректных элементов будет равно 1. Если эти тесты выполняются верно, то можно быть уверен+ ным, что программа правильно выбирает недостоверные данные внутри объектов:

function testInvalid0()

 

 

 

{

 

 

 

$ul = new UserLog (array ('site'

=> 0,

 

'section'

 

=> 9,

'login'

 

=>

"user101",

'session'

=>

"1E23553",

'firstname' =>

"Alice",

'lastname'

=>

"AppleGate",

'address1'

=>

"123Main",

'city'

 

=>

"Sandusky",

'state'

 

=>

"OH",

'zip'

 

=>

"44870",

'demo0'

 

=>

"21",

'demo1'

 

=>

"15",

'demo2'

 

=>

"22",

'demo3'

 

=>

"26"));

$this->assertEquals(false, $ul->isValid(), "valid ul"); $this->assertEquals(1, count ($ul->getInvalidData()), "valid site");

}

Третий тест также проверяет объект $ulBad. Используются утверждения о том, что данные недостоверны и что количество некорректных элементов равно трем:

function testInvalid1()

{

$this->assertEquals(false, $this->ulBad->isValid(), "invalid ul"); $this->assertEquals(3, count ($this->ulBad->getInvalidData()),

"invalid site, section, and login");

}

Предыдущие тесты обрабатывали имеющиеся данные, содержащиеся внутри объек+ тов. Однако как проверить следующий этап, т.е. сохранение объектов в базе данных? Для этого, естественно, придется разработать еще несколько тестов. В следующем тесте используется блок try-catch, а также метод assert(). Тест представляет собой по+ пытку отправить в базу данных информацию, содержащуюся в объекте $ulGood; если попытка удалась, то ошибок нет и утверждение истинно. В противном случае методу assert() передается значение false, а на экран выводится сообщение об ошибке.

Следует заметить, что метод assert() не используется для отображения сообще+ ния об ошибке. Это связано с тем, что желательно получить более подробное описа+ ние возникшей ошибки. Поэтому в данном случае используется сообщение, сгенери+ рованное в блоке try:

Учебный пример: диспетчер протоколирования на PHP 675

function testGoodPersist0()

{

try { $this->ulGood->persist(); $this->assert(true);

} catch (MultiLogException $e) { $this->assert(false);

print ($e->getErrorMessage());

}

}

Теперь следует точно так же проверить объект $ulBad. Естественно, ожидается, что проверка выдаст отрицательный результат, поэтому утверждения необходимо со+ ответствующим образом изменить:

function testBadPersist0()

{

try {

$this->ulBad->persist();

$this->assert(false); // здесь должен быть сбой } catch (MultiLogInvalidDataException $e) {

$this->assert(true);

}

}

Конечно, программа не должна сохранять один и тот же объект дважды в одной строке. Чтобы протестировать такую ситуацию, следует сформулировать утверждение о том, что вторая попытка сохранения не удалась. После этого вторая попытка сохра+ нения приведет к возникновению исключения, и это можно будет использовать для формулировки правильного утверждения:

function testBadPersist1()

{

try { $this->ulGood->persist(); $this->assert(true);

$this->ulGood->persist();

$this->assert(false); // здесь должен быть сбой

}catch (MultiLogInvalidDatabaseException $e) { $this->assert(true);

}catch (MultiLogDatabaseQueryException $e) {

$this->assert(false); // неверное утверждение

}

}

А что если приложение работает не так, как должно? Например, в коде была слу+ чайно допущена опечатка. Чтобы проверить это, вставим намеренно слово ‘‘where’’ в значение сеанса. При попытке сохранения таких данных должно быть сгенерирова+ но исключение.

В блоке catch результат метода $ul->persist() ошибочно принимается равным true. Это должно привести к отрицательному результату тестирования (вывод будет показан в конце этого раздела):

function testBadPersist2_fail()

 

{

 

$ul = new UserLog(array('site'

=> "1",

'section' => "9",

676 Глава 17

'login' => "user101",

'session' => "1' where '1' and '1"));

try {

$ul->persist();

$this->assert(false); // здесь должен быть сбой } catch (MultiLogDatabaseQueryException $e) {

$this->assert(true);

}

}

Это, конечно, не единственный способ использования утверждений. В следующем примере показано, как вставить в метод assert() любой оператор ++++++ при условии, что он возвращает булево значение. Кроме того, в этом примере метод assert()используется для вывода сообщения об ошибке.

В целях эксперимента сформируем некорректный SQL+оператор. Например, мож+ но заменить слово ‘‘insert’’ на ‘‘inert’’:

function testSqlGeneration0()

{

$this->assert(

strpos (" ".$this->ulGood->toSQL(), "insert into user_log(visit_date,

visit_time, site_id, demo_id, login_id, session, firstname,

lastname, address1, address2, city, state, zip )") > 0, "invalid SQL generation first");

$this->assert(

strpos (" ".$this->ulGood->toSQL(), "1, 2, '3', '1E23553', 'Alice', 'AppleGate', '123Main', ", 'Sandusky', 'OH',

'44870' )") > 0, "invalid SQL generation last");

}

Теперь проверим два других метода ++++++ функции toHTML() и getDemographics(). В формулировке утверждений для этого теста нет ничего нового:

function testHtmlGeneration0 ()

{

$this->assert(strpos (" ".$this->ulGood->toHTML(), "<tr") > 0); $this->assert(strpos ($this->ulGood->toHTML(), ">AppleGate</td><td")

> 0);

}

function testDemo0 ()

{

$this->assertEquals(4, count($this->ulGood->getDemographics())); $this->assertEquals(4, count($this->ulBad->getDemographics()));

}

В последнем тесте заново создается база данных, снова сохраняется объект $ulGood, а затем база данных опрашивается вручную, чтобы определить количество возвращенных значений. Этот тест подтверждает результаты предыдущего теста:

function testDemo1 ()

{

include ("../logs/initialize.php"); // заново создаем базу

try { $this->ulGood->persist(); $this->assert(true);

Учебный пример: диспетчер протоколирования на PHP 677

} catch (MultiLogException $e) { $this->assert(false);

print ($e->getErrorMessage()); return;

}

$this->db = LogUtils::openDatabase();

$query = LogUtils::executeQuery ($this->db,

"SELECT COUNT(*) FROM user_demographics");

$row = LogUtils::getQueryArray($query); $this->assertEquals ("4", $row[0]);

}

}

Все тесты разработаны, поэтому можно объединить их в тестовый комплект, так чтобы они выполнялись все вместе:

$suite = new TestSuite;

$suite->addTest(new TestUserLog("testValid0")); $suite->addTest(new TestUserLog("testInvalid0")); $suite->addTest(new TestUserLog("testInvalid1")); $suite->addTest(new TestUserLog("testGoodPersist0")); $suite->addTest(new TestUserLog("testBadPersist0")); $suite->addTest(new TestUserLog("testBadPersist1")); $suite->addTest(new TestUserLog("testBadPersist2_fail")); $suite->addTest(new TestUserLog("testSqlGeneration0")); $suite->addTest(new TestUserLog("testHtmlGeneration0")); $suite->addTest(new TestUserLog("testDemo0")); $suite->addTest(new TestUserLog("testDemo1"));

Следующий код создает новый объект класса TestRunner и вызывает метод run(), который выполняет все тесты и отображает их результаты:

$testRunner = new TestRunner(); $testRunner->run( $suite );

Первый тестовый комплект готов, и можно проанализировать его результаты. Откройте в браузере страницу test.UserLog.php (рис. 17.5) и перейдите вниз ++++++

отображается ошибка, сгенерированная testBadPersist2_fail(). Следует заме+ тить, что PHPUnit распечатывает стандартные сообщения ok и FAIL зелеными и красными буквами соответственно в зависимости от того, как выполнился тест: так, как ожидалось, или нет.

Следующий тестовый класс создается так же, как и предыдущий ++++++ объявляются классы, к которым необходим доступ:

<?php

require_once ("../settings-test.php"); // это важно! require_once ("class.UserLog.php");

require_once ("class.LogContainer.php"); require_once ("phpunit/phpunit.php");

Так как тестируется класс LogContainer, необходимо включить несколько UserLog+ объектов для проверки внутри контекста. В этом примере создается и сохраняется в базе три новых UserLog+объекта:

678 Глава 17

Рис. 17.5.

class TestLogContainer extends TestCase { function setUp()

{include ("../logs/initialize.php"); // заново создаем базу $ul0 = new UserLog (

array ('site'

=> "1",

'section'

=> "111",

'login'

=> "aapple",

'session'

=> "1E23553",

'firstname'

=> "Aurthor",

'lastname'

=> "Andersen",

'address1'

=> "123 Main St.",

'city'

=> "Sandusky",

'state'

=> "OH",

'zip'

=> "44870",

'demo0'

=> "21",

'demo1'

=> "15",

'demo2'

=> "22",

'demo3'

=> "26"));

$ul0->persist();

 

Учебный пример: диспетчер протоколирования на PHP 679

$ul1 = new UserLog (

 

array ('site'

=> "1",

'section'

=> "112",

'login'

=> "bbarker",

'session'

=> "3F398",

'firstname'

=> "Blob",

'lastname'

=> "Barker",

'address1'

=> "100 Hollywood Blvd.",

'city'

=> "LA",

'state'

=> "CA",

'zip'

=> "90036",

'demo0'

=> "78",

'demo1'

=> "21",

'demo2'

=> "12",

'demo3'

=> "7"));

$ul1->persist();

 

$ul2 = new UserLog (

 

array ('site'

=> "2",

'section'

=> "200",

'login'

=> "ccabbage",

'session'

=> "L33T",

'firstname'

=> "Capitan",

'lastname'

=> "Cabbage",

'address1'

=> "55 Broadway",

'city'

=> "NYC",

'state'

=> "NY",

'zip'

=> "10001",

'demo0'

=> "14",

'demo1'

=> "15",

'demo2'

=> "41",

'demo3'

=> "0"));

$ul2->persist();

 

}

Имея специфический контекст, можно создать тест. Для этого примера включает+ ся только один тест. Он создает новый LogContainer+объект, содержащий все UserLog+ объекты, поступающие с сайта 1:

function testContainerValid0()

{

try {

$lc = new LogContainer ("", "", "1", "");

Зная контекст, сформулируем утверждение о том, что внутри LogContainer со+ держится только два UserLog+объекта:

$this->assertEquals(2, $lc->getCount(), "count");

Затем создается утверждение о том, что два UserLog+объекта, содержащиеся в LogContainer, имеют регистрационные имена aapple и bbarker соответственно:

$logs = $lc->getUserLogs();

$this->assert(strcmp ($logs[0]->getLogin, "aapple"), "login_id incorrect a");

$this->assert(strcmp ($logs[0]->getLogin, "bbarker"), "login_id incorrect b");

} catch (MultiLogException $e) { $this->assert(false);

print $e->getErrorMessage();

680 Глава 17

}

}

}

Наконец, создается и запускается тестовый комплект:

$suite = new TestSuite;

$suite->addTest(new TestLogContainer("testContainerValid0"));

$testRunner = new TestRunner(); $testRunner->run( $suite );

В браузере результат работы этого сценария должен выглядеть аналогично рис. 17.6.

Рис. 17.6.

Последний тестовый класс проверяет демографические данные в UserLog+ объектах. Сначала подключаются необходимые файлы:

<?php

require_once ("../settings-test.php"); require_once ("class.UserDemographic.php"); require_once ("phpunit/phpunit.php");

Объявление класса начинается с определения переменных, в которых будут хра+ ниться UserDemographic+объекты. Кроме того, объявляется функция intializeDb(), которая будет использоваться для повторного заполнения базы данных ++++++ это позво+ лит переустанавливать контекст:

class TestUserDemographic extends TestCase {private $demoGood = null;

private $demoBad = null;

function initializeDb () {

include "../logs/initialize.php";

}

Учебный пример: диспетчер протоколирования на PHP 681

Затем контекст инициализируется ++++++ следует заметить, что демографические дан+ ные содержатся только в последних элементах массива, поэтому можно спокойно иг+ норировать первые элементы каждого UserLog+объекта:

function setUp()

 

 

{

 

 

$this->demoGood = new UserDemographic (

// игнорируется

array ('id'

=> 1,

'site'

=> 1,

// игнорируется

'section'

=> 2,

// игнорируется

'login'

=> "3",

// игнорируется

'session'

=> "1E23553",

// игнорируется

'firstname'

=> "Alice",

// игнорируется

'lastname'

=> "AppleGate",

// игнорируется

'address1'

=> "123Main",

// игнорируется

'city'

=> "Sandusky",

// игнорируется

'state'

=> "OH",

// игнорируется

'zip'

=> "44870",

// игнорируется

'demo'

=> "21",

 

'demo1'

=> "15",

 

'demo2'

=> "22",

 

'demo3'

=> "26"));

 

$this->demoBad = new UserDemographic (

// игнорируется

array ('site'

=> 1,

'section'

=> 2,

// игнорируется

'login'

=> "3",

// игнорируется

'session'

=> "1E23553",

// игнорируется

'firstname'

=> "Alice",

// игнорируется

'lastname'

=> "AppleGate",

// игнорируется

'address1'

=> "123Main",

// игнорируется

'city'

=> "Sandusky",

// игнорируется

'state'

=> "OH",

// игнорируется

'zip'

=> "44870",

// игнорируется

'demo1'

=> "15",

 

'demo2'

=> "22",

 

'demo3'

=> "26"));

 

}

 

 

 

 

 

Первый тест для UserDemographic+объектов выглядит так:

function testValid0()

{

$this->assertEquals(null, $this->demoGood->city, "invalid city"); $this->assertEquals("21", $this->demoGood->demo, "invalid demo0"); $this->assertEquals(null, $this->demoGood->demo1, "invalid demo1"); $this->assertEquals(null, $this->demoGood->demo2, "invalid demo2"); $this->assertEquals(null, $this->demoGood->demo3, "invalid demo3"); $this->assertEquals(null, $this->demoGood->demo4, "invalid demo4"); $this->assertEquals(true, $this->demoGood->isValid(), "valid log"); $this->assertEquals(0, count ($this->demoGood->getInvalidData()),

"valid count");

}

Приведенный ниже тест позволяет убедиться, что предположительно недосто+ верные данные действительно недостоверны:

function testInvalid0()

{

$this->assertEquals(false, $this->demoBad->isValid(), "invalid log"); $this->assertEquals(1, count ($this->demoBad->getInvalidData()),

"expecting 1 invalid item");

}

682 Глава 17

Наконец, создается тест для проверки возможности сохранения объекта в базе данных:

function testGoodPersist0()

{

$this->initializeDb(); try {

$this->demoGood->persist(); $this->assert(true);

} catch (MultiLogException $e) { $this->assert(false);

print ($e->getErrorMessage());

}

}

}

В конце используется уже знакомый код:

$suite = new TestSuite;

$suite->addTest(new TestUserDemographic("testValid0")); $suite->addTest(new TestUserDemographic("testInvalid0")); $suite->addTest(new TestUserDemographic("testGoodPersist0"));

$testRunner = new TestRunner(); $testRunner->run( $suite );

Результат работы тестов показан на рис. 17.7.

Рис. 17.7.

Стоит отметить, что пакет PHPUnit содержит множество других функций. В главе были описаны функции, с помощью которых можно начинать работу с пакетом, но можно использовать и другие функции для создания необходимых тестов ++++++ изучение этих функций остается в качестве упражнения читателям.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]