SlideShare a Scribd company logo
TTD in ZF2

Пространства имен

Замыкания

ZF2 поставляемые в стандартной
поставке тесты

Поддержка нового phpunit 3.5
# ls
Bootstrap.php docs phpunit.xml resources runtests.sh
TestConfiguration.php.dist Zend
# cat TestConfiguration.php.dist
/**
* Zend_Db_Adapter_Sqlsrv
* Note: Make sure that you create the "test" database and set a
* username and password
*
*/
define('TESTS_ZEND_DB_ADAPTER_SQLSRV_ENABLED', false);
define('TESTS_ZEND_DB_ADAPTER_SQLSRV_HOSTNAME', '');
define('TESTS_ZEND_DB_ADAPTER_SQLSRV_USERNAME', null);
define('TESTS_ZEND_DB_ADAPTER_SQLSRV_PASSWORD', null);
define('TESTS_ZEND_DB_ADAPTER_SQLSRV_DATABASE', 'test');
Стукрутра папок
- library - модульные
- application - интеграционные
+ functional - функциональные
+ perfomance — функциональные
Тестировать не нужно
- Zend Framework
- Doctrine / Propel
- Jquery / Prototype / Dojo
- Symphony DI / Crafty / DI
- all extenal libraries ...
Тестировать нужно
- Абстрактные классы и интефейсы
- Базовые компонеты
- Магическую инициализацию
- Любые не очевидные вещи
DRY – не повторяться
1 тест – 1 проверка
Хороший тест – 5 строк
Если что-то протестировано, не
тетсируем это снова
Крохобор (The Nitpicker) Уклонист (The Dodger)
Гигант (Giant) Любитель Порядка (The Sequencer) Счётчик (The Enumerator)
Избранный (The One) Тормоз (The Slow Poke) ...
Основные принципы
Class => ClassName + Test.php
Namespace => Test
PhpDoc => bugNumber
_files — для тестовых файлов
_fixtures — для фикстур
phpunit.xml
@cover Class::method
Организация тестов
<phpunit colors="true" bootstrap="./bootstrap.php">
<testsuite name="Test/Library">
<directory>./library/</directory>
</testsuite>
<testsuite name="Test/Application">
<directory>./application/</directory>
</testsuite>
<testsuite name="Test/Functional">
<directory>./functional/</directory>
</testsuite>
<logging>
<log type="coverage-html" target="/tmp" charset="UTF-8"
yui="true" highlight="false"
lowUpperBound="35" highLowerBound="70"/>
</logging>
</phpunit>
/test/tests/application/controllers# cat IndexControllerTest.php
namespace Test;
class IndexController extends ZendTestPHPUnitControllerTestCase
{
public $bootstrap = '../../bootstrap.php';
/**
* @group Bug123
* @cover IndexController::indexAction
*/
public function testIndexAction()
{
$this->dispatch('/');
$this->assertController("index");
$this->assertAction("index");
}
}
Тестирование Controller
Тестирование Controller
class IndexController extends ZendTestPHPUnitControllerTestCase
public function testIndexAction()
$this->dispatch('/');
$this->assertController("index");
$this->assertAction("index");
$this->assertQueryContentContains('#content', 'Hello Im here');
$this->assertQueryContentContains('div.content', 'Hello Im here');
$this->assertQueryContentContains('body .content', 'Hello Im here');
$this->assertQueryCount('#categories li', 3);
<?php
class IndexController extends AbstractController
public function indexAction()
$this->view->hello = 'Im here';
Тестирование Controller::init
<?php
abstract class AbstractController extends ZendControllerAction
{
public function init()
{
$this->_helper->contextSwitch()
->addActionContext('index', array('xml', 'json'))
->setAutoJsonSerialization(true)
->initContext();
$this->view->categories = new ApplicationModelCategoryMapper();
}
}
Тестирование Controller:init
class AbstractController extends ZendTestPHPUnitControllerTestCase
public function testInitContext()
$controller = new AbstractControllerStub($this->getRequest(),
$this->getResponse());
$this->assertEquals(
$controller->getHelper('ContextSwitch')->getActionContexts('index'),
array('xml', 'json'));
public function testInitCategories()
$controller = new AbstractControllerStub($this->getRequest(),
$this->getResponse());
$this->assertType('ApplicationModelCategoryMapper',
$controller->view->categories);
Тестирование Form
<?php
namespace ApplicationForm;
class Registration extends ZendFormForm
public function init()
$this->addElement('text', 'username');
$this->addElement('text', 'password');
$this->addElement('text', 'password_retype');
public function isValid($params)
$result = parent::isValid($params);
if ($this->getValue('password')!=$this->getValue('password_retype')) {
return false;
}
return $result;
Тестирование Form
class Registration extends PHPUnit_Framework_TestCase
public function testValidate()
$form = new ApplicationFormRegistration;
$this->assertTrue($form->isValid(array(
'username' => 'test',
'password' => '123',
'password_retype' => '123',
)));
public function testValidateFail()
$form = new ApplicationFormRegistration;
$this->assertFalse($form->isValid(array(
'username' => 'test',
'password' => '123',
'password_retype' => '1234',
)));
Архитектура
→ getConnection
→ getDataSet
→ createDbTableDataSet
→ createDbTable
→ createDbRowset
Тестирование DbTable
$categories = new ApplicationModelDbTableCategories($this->getAdapter());
$categories->insert(array('id'=>4, 'name'=>'test'));
$this->assertDataSetsEqual(
$this->createFlatXmlDataSet("_fixtures/categoriesInsert.xml"),
$this->createDbTableDataSet(array($categories))
);
Тестирование DbTable
class CategoriesTest extends ZendTestPHPUnitDatabaseTestCase
public function getConnection()
$application = new Application (_ENV, _PATH . '/configs/application.ini');
$application->bootstrap();
$resource = $application->getBootstrap()->getPluginResource('db');
return $this->createZendDbConnection($resource->getDbAdapter(), 'any');
public function getDataSet()
return $this->createFlatXMLDataSet(__DIR__ . '/_fixtures/categories.xml');
public function testFecthAll()
$categories = new ApplicationModelDbTableCategories($this->getAdapter());
$rowset = $categories->fetchAllOrdered();
$this->assertEquals(count($rowset), 3);
Тестирование DbTable
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<categories id="1" name="cat1" />
<categories id="3" name="cat3" />
<categories id="2" name="cat2" />
</dataset>
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<categories id="1" name="cat1" />
<categories id="3" name="cat3" />
<categories id="2" name="cat2" />
<categories id="4" name="test" />
</dataset>
Тестирование Mapper
1. Использует сервис/сервисы
2. Используется моделями
3. Содержит код
setTable/getTable/__construct/init
которые идентичны и миргурируют
из одного маппера в другой
+ есть еще внешние источники
данных SOAP/XML-RPC/REST/RSS
Тестирование Mapper
<?php
namespace ApplicationModel;
class XXXMapper
{
protected $_table = null;
protected $_defaultServiceName = '';
public function __construct(DbTable $table=null)
public function getTable()
public function setTable(DbTable $table)
…
}
Тестирование Mapper

Выделить общий класс
AbstractMapper

Выделить интерфейс для сервисов

Наследовать все мапперы от
абстрактного

Имплементировать интерфейс
Service в DbTable
Тестирование Mapper
<?php
namespace ApplicationModel;
class AbstractMapper
{
protected $_service = null;
protected $_defaultServiceName = '';
public function __construct(Service $service=null)
{
if (is_null($service)) {
$name = $this->_defaultServiceName;
$this->setService(new $name);
} else {
$this->setService($this->_service = $service);
}
}
public function getService()
public function setService(Service $service)
}
Тестирование Mapper

Создаем реализации

Складываем в подпапку _files в
текущей директории
__Construct / Abstract class / Interface
<?php
namespace Test;
class AbstractMapperMockAutoload extends ApplicationModelAbstractMapper
{
protected $_defaultServiceName = 'TestServiceMock';
}
Тестирование Mapper
class AbstractMapperTest extends PHPUnit_Framework_TestCase
public function testInit()
$mock = new AbstractMapperMockAutoload();
$this->assertType('TestServiceMock', $mock->getService());
public function testInitFail()
try {
$mock = new AbstractMapperMock();
} catch (Exception $e) {
return ;
}
$this->fail('An expected exception has not been raised.');
public function testSet()
$mock = new AbstractMapperMockAutoload();
$mock->setService(new ServiceMock());
$this->assertType('TestServiceMock', $mock->getService());
Тестирование Selenium
Selenium RC
Selenium IDE
Selenium GRID
Тестирование Selenium
Тестирование Selenium
Native = left
<div id=”left”> <input name='left'>
Xpath = //div[@id='left']
<div id=”left”>
Css =
css=ul#recordlist li:nth-child(4)
css=input[name='continue'][type='button']
css=a[id^='id_prefix_']
css=a:contains('Log Out')
Тестирование Selenium
class Example extends PHPUnit_Extensions_SeleniumTestCase
{
protected $captureScreenshotOnFailure = TRUE;
protected $screenshotPath = '~/conf/public/screenshots';
protected $screenshotUrl = 'http://conf.local/screenshots';
protected function setUp()
{
$this->setBrowser("*chrome");
$this->setBrowserUrl("http://conf.local/");
}
public function testMyTestCase()
{
$this->open("index/index");
$this->assertEquals("index", $this->getText("content"));
}
}
Тестирование Selenium
public function testAjaxLoading()
{
$this->open("/index/index/");
$this->assertEquals("index", $this->getText("content"));
$this->click("showIndexInAjax");
for ($second = 0; ; $second++) {
if ($second >= 60) $this->fail("timeout");
try {
if ("Im here" == $this->getText("content")) break;
} catch (Exception $e) {}
sleep(1);
}
$this->assertEquals("Im here", $this->getText("content"));
}
Тестирование Selenium
class IndexController extends PHPUnit_Extensions_SeleniumTestCase
{
public function testHighlight()
{
$this->open("/index/index/");
$this->mouseOver("//div[@id='left']/li");
$this->assertEquals("rgb(255, 0, 0)", $this->getEval(
"this.findEffectiveStyleProperty(this.page()
.findElement("//div[@id='left']/li"),
'backgroundColor')"));
}
Тестирование Selenium
Вопросы ?

More Related Content

PDF
Юнит тестирование в Zend Framework 2.0
PDF
Sis quiz
PDF
Як досвід компанії перетворився на фреймворк
DOC
Dwr实战
PDF
Palestra PythonBrasil[8]
DOCX
Documentacion edderson callpa_ortiz
PDF
PPT
Palestra sobre MongoDB com PHP no PHP'n'Rio
Юнит тестирование в Zend Framework 2.0
Sis quiz
Як досвід компанії перетворився на фреймворк
Dwr实战
Palestra PythonBrasil[8]
Documentacion edderson callpa_ortiz
Palestra sobre MongoDB com PHP no PHP'n'Rio

What's hot (19)

PDF
jQuery for beginners
PDF
2km Workshop: Desenvolvimento ágil com o CakePHP
PDF
Aller plus loin avec Doctrine2
PDF
PDF
from new class and dependency injection to PSR-11 and Auto-wiring
PDF
Quiz Component For Joomla
PPT
Wek14 mysql 2
PDF
Working With Ajax Frameworks
TXT
Index2
PDF
Twig, los mejores trucos y técnicas avanzadas
TXT
KvZ Web Tasarım Hizmetleri
DOCX
Dennis zapana perez
PDF
Testování prakticky
PDF
Introducción a Bolt
PDF
Drupal Cms Prezentace
PPTX
es6.concurrency()
DOCX
Php codigos interfaces fredy guzman cusihunca
PDF
Silex al límite
PDF
Assalamualaykum warahmatullahi wabarakatuu
jQuery for beginners
2km Workshop: Desenvolvimento ágil com o CakePHP
Aller plus loin avec Doctrine2
from new class and dependency injection to PSR-11 and Auto-wiring
Quiz Component For Joomla
Wek14 mysql 2
Working With Ajax Frameworks
Index2
Twig, los mejores trucos y técnicas avanzadas
KvZ Web Tasarım Hizmetleri
Dennis zapana perez
Testování prakticky
Introducción a Bolt
Drupal Cms Prezentace
es6.concurrency()
Php codigos interfaces fredy guzman cusihunca
Silex al límite
Assalamualaykum warahmatullahi wabarakatuu
Ad

Viewers also liked (18)

PPTX
1000 миллисекунд из жизни Magento
ODP
Мобильные клиенты интернет-магазинов
PDF
NoSQL и Zend Framework (Никита Грошин)
PPTX
Все дороги ведут в Checkout
PDF
Эволюция ZF: архитектура, шаблоны, рефакторинг
PPTX
Применение компонент-ориентированной архитектуры для написания Magento Extens...
PDF
Хранение, обработка и отдача статики с использованием \Zend\File. Опыт социал...
PDF
Применение Scrum и Kanban для разработки web-приложений
PDF
Doctrine 2
PPT
Система рендеринга в Magento
PPTX
Реализация шаблонов корпоративных приложений в Magento
PPTX
Преимущества использования полнотекстового поиска в интернет-магазинах
PDF
Встречайте Zend Framework 2.0
PPTX
Ключ успеха – процесс или продукт?
PPT
Управление продуктом в стиле Magento Unified Process
PPTX
Применение TDD при разработке веб-сервисов
PPTX
Индексирование в Magento
PDF
NoSQL и Zend Framework (Ростислав Михайлив)
1000 миллисекунд из жизни Magento
Мобильные клиенты интернет-магазинов
NoSQL и Zend Framework (Никита Грошин)
Все дороги ведут в Checkout
Эволюция ZF: архитектура, шаблоны, рефакторинг
Применение компонент-ориентированной архитектуры для написания Magento Extens...
Хранение, обработка и отдача статики с использованием \Zend\File. Опыт социал...
Применение Scrum и Kanban для разработки web-приложений
Doctrine 2
Система рендеринга в Magento
Реализация шаблонов корпоративных приложений в Magento
Преимущества использования полнотекстового поиска в интернет-магазинах
Встречайте Zend Framework 2.0
Ключ успеха – процесс или продукт?
Управление продуктом в стиле Magento Unified Process
Применение TDD при разработке веб-сервисов
Индексирование в Magento
NoSQL и Zend Framework (Ростислав Михайлив)
Ad

Юнит тестирование в Zend Framework 2.0

  • 2.  Пространства имен  Замыкания  ZF2 поставляемые в стандартной поставке тесты  Поддержка нового phpunit 3.5
  • 3. # ls Bootstrap.php docs phpunit.xml resources runtests.sh TestConfiguration.php.dist Zend # cat TestConfiguration.php.dist /** * Zend_Db_Adapter_Sqlsrv * Note: Make sure that you create the "test" database and set a * username and password * */ define('TESTS_ZEND_DB_ADAPTER_SQLSRV_ENABLED', false); define('TESTS_ZEND_DB_ADAPTER_SQLSRV_HOSTNAME', ''); define('TESTS_ZEND_DB_ADAPTER_SQLSRV_USERNAME', null); define('TESTS_ZEND_DB_ADAPTER_SQLSRV_PASSWORD', null); define('TESTS_ZEND_DB_ADAPTER_SQLSRV_DATABASE', 'test');
  • 4. Стукрутра папок - library - модульные - application - интеграционные + functional - функциональные + perfomance — функциональные
  • 5. Тестировать не нужно - Zend Framework - Doctrine / Propel - Jquery / Prototype / Dojo - Symphony DI / Crafty / DI - all extenal libraries ...
  • 6. Тестировать нужно - Абстрактные классы и интефейсы - Базовые компонеты - Магическую инициализацию - Любые не очевидные вещи
  • 7. DRY – не повторяться 1 тест – 1 проверка Хороший тест – 5 строк Если что-то протестировано, не тетсируем это снова Крохобор (The Nitpicker) Уклонист (The Dodger) Гигант (Giant) Любитель Порядка (The Sequencer) Счётчик (The Enumerator) Избранный (The One) Тормоз (The Slow Poke) ... Основные принципы
  • 8. Class => ClassName + Test.php Namespace => Test PhpDoc => bugNumber _files — для тестовых файлов _fixtures — для фикстур phpunit.xml @cover Class::method Организация тестов
  • 9. <phpunit colors="true" bootstrap="./bootstrap.php"> <testsuite name="Test/Library"> <directory>./library/</directory> </testsuite> <testsuite name="Test/Application"> <directory>./application/</directory> </testsuite> <testsuite name="Test/Functional"> <directory>./functional/</directory> </testsuite> <logging> <log type="coverage-html" target="/tmp" charset="UTF-8" yui="true" highlight="false" lowUpperBound="35" highLowerBound="70"/> </logging> </phpunit>
  • 10. /test/tests/application/controllers# cat IndexControllerTest.php namespace Test; class IndexController extends ZendTestPHPUnitControllerTestCase { public $bootstrap = '../../bootstrap.php'; /** * @group Bug123 * @cover IndexController::indexAction */ public function testIndexAction() { $this->dispatch('/'); $this->assertController("index"); $this->assertAction("index"); } } Тестирование Controller
  • 11. Тестирование Controller class IndexController extends ZendTestPHPUnitControllerTestCase public function testIndexAction() $this->dispatch('/'); $this->assertController("index"); $this->assertAction("index"); $this->assertQueryContentContains('#content', 'Hello Im here'); $this->assertQueryContentContains('div.content', 'Hello Im here'); $this->assertQueryContentContains('body .content', 'Hello Im here'); $this->assertQueryCount('#categories li', 3); <?php class IndexController extends AbstractController public function indexAction() $this->view->hello = 'Im here';
  • 12. Тестирование Controller::init <?php abstract class AbstractController extends ZendControllerAction { public function init() { $this->_helper->contextSwitch() ->addActionContext('index', array('xml', 'json')) ->setAutoJsonSerialization(true) ->initContext(); $this->view->categories = new ApplicationModelCategoryMapper(); } }
  • 13. Тестирование Controller:init class AbstractController extends ZendTestPHPUnitControllerTestCase public function testInitContext() $controller = new AbstractControllerStub($this->getRequest(), $this->getResponse()); $this->assertEquals( $controller->getHelper('ContextSwitch')->getActionContexts('index'), array('xml', 'json')); public function testInitCategories() $controller = new AbstractControllerStub($this->getRequest(), $this->getResponse()); $this->assertType('ApplicationModelCategoryMapper', $controller->view->categories);
  • 14. Тестирование Form <?php namespace ApplicationForm; class Registration extends ZendFormForm public function init() $this->addElement('text', 'username'); $this->addElement('text', 'password'); $this->addElement('text', 'password_retype'); public function isValid($params) $result = parent::isValid($params); if ($this->getValue('password')!=$this->getValue('password_retype')) { return false; } return $result;
  • 15. Тестирование Form class Registration extends PHPUnit_Framework_TestCase public function testValidate() $form = new ApplicationFormRegistration; $this->assertTrue($form->isValid(array( 'username' => 'test', 'password' => '123', 'password_retype' => '123', ))); public function testValidateFail() $form = new ApplicationFormRegistration; $this->assertFalse($form->isValid(array( 'username' => 'test', 'password' => '123', 'password_retype' => '1234', )));
  • 17. → getConnection → getDataSet → createDbTableDataSet → createDbTable → createDbRowset Тестирование DbTable $categories = new ApplicationModelDbTableCategories($this->getAdapter()); $categories->insert(array('id'=>4, 'name'=>'test')); $this->assertDataSetsEqual( $this->createFlatXmlDataSet("_fixtures/categoriesInsert.xml"), $this->createDbTableDataSet(array($categories)) );
  • 18. Тестирование DbTable class CategoriesTest extends ZendTestPHPUnitDatabaseTestCase public function getConnection() $application = new Application (_ENV, _PATH . '/configs/application.ini'); $application->bootstrap(); $resource = $application->getBootstrap()->getPluginResource('db'); return $this->createZendDbConnection($resource->getDbAdapter(), 'any'); public function getDataSet() return $this->createFlatXMLDataSet(__DIR__ . '/_fixtures/categories.xml'); public function testFecthAll() $categories = new ApplicationModelDbTableCategories($this->getAdapter()); $rowset = $categories->fetchAllOrdered(); $this->assertEquals(count($rowset), 3);
  • 19. Тестирование DbTable <?xml version="1.0" encoding="UTF-8"?> <dataset> <categories id="1" name="cat1" /> <categories id="3" name="cat3" /> <categories id="2" name="cat2" /> </dataset> <?xml version="1.0" encoding="UTF-8"?> <dataset> <categories id="1" name="cat1" /> <categories id="3" name="cat3" /> <categories id="2" name="cat2" /> <categories id="4" name="test" /> </dataset>
  • 20. Тестирование Mapper 1. Использует сервис/сервисы 2. Используется моделями 3. Содержит код setTable/getTable/__construct/init которые идентичны и миргурируют из одного маппера в другой + есть еще внешние источники данных SOAP/XML-RPC/REST/RSS
  • 21. Тестирование Mapper <?php namespace ApplicationModel; class XXXMapper { protected $_table = null; protected $_defaultServiceName = ''; public function __construct(DbTable $table=null) public function getTable() public function setTable(DbTable $table) … }
  • 22. Тестирование Mapper  Выделить общий класс AbstractMapper  Выделить интерфейс для сервисов  Наследовать все мапперы от абстрактного  Имплементировать интерфейс Service в DbTable
  • 23. Тестирование Mapper <?php namespace ApplicationModel; class AbstractMapper { protected $_service = null; protected $_defaultServiceName = ''; public function __construct(Service $service=null) { if (is_null($service)) { $name = $this->_defaultServiceName; $this->setService(new $name); } else { $this->setService($this->_service = $service); } } public function getService() public function setService(Service $service) }
  • 24. Тестирование Mapper  Создаем реализации  Складываем в подпапку _files в текущей директории __Construct / Abstract class / Interface <?php namespace Test; class AbstractMapperMockAutoload extends ApplicationModelAbstractMapper { protected $_defaultServiceName = 'TestServiceMock'; }
  • 25. Тестирование Mapper class AbstractMapperTest extends PHPUnit_Framework_TestCase public function testInit() $mock = new AbstractMapperMockAutoload(); $this->assertType('TestServiceMock', $mock->getService()); public function testInitFail() try { $mock = new AbstractMapperMock(); } catch (Exception $e) { return ; } $this->fail('An expected exception has not been raised.'); public function testSet() $mock = new AbstractMapperMockAutoload(); $mock->setService(new ServiceMock()); $this->assertType('TestServiceMock', $mock->getService());
  • 28. Тестирование Selenium Native = left <div id=”left”> <input name='left'> Xpath = //div[@id='left'] <div id=”left”> Css = css=ul#recordlist li:nth-child(4) css=input[name='continue'][type='button'] css=a[id^='id_prefix_'] css=a:contains('Log Out')
  • 29. Тестирование Selenium class Example extends PHPUnit_Extensions_SeleniumTestCase { protected $captureScreenshotOnFailure = TRUE; protected $screenshotPath = '~/conf/public/screenshots'; protected $screenshotUrl = 'http://conf.local/screenshots'; protected function setUp() { $this->setBrowser("*chrome"); $this->setBrowserUrl("http://conf.local/"); } public function testMyTestCase() { $this->open("index/index"); $this->assertEquals("index", $this->getText("content")); } }
  • 30. Тестирование Selenium public function testAjaxLoading() { $this->open("/index/index/"); $this->assertEquals("index", $this->getText("content")); $this->click("showIndexInAjax"); for ($second = 0; ; $second++) { if ($second >= 60) $this->fail("timeout"); try { if ("Im here" == $this->getText("content")) break; } catch (Exception $e) {} sleep(1); } $this->assertEquals("Im here", $this->getText("content")); }
  • 31. Тестирование Selenium class IndexController extends PHPUnit_Extensions_SeleniumTestCase { public function testHighlight() { $this->open("/index/index/"); $this->mouseOver("//div[@id='left']/li"); $this->assertEquals("rgb(255, 0, 0)", $this->getEval( "this.findEffectiveStyleProperty(this.page() .findElement("//div[@id='left']/li"), 'backgroundColor')")); }