Top Banner
TTD in ZF2
32

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

Jan 15, 2015

Download

Technology

zfconfua

Ростислав Михайлив
Chief Developer / Software Architect, Gadu-Gadu
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Юнит тестирование в Zend Framework 2.0

TTD in ZF2

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

Пространства имен Замыкания ZF2 поставляемые в стандартной поставке тесты Поддержка нового phpunit 3.5

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

# lsBootstrap.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');

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

Стукрутра папок

- library - модульные- application - интеграционные+ functional - функциональные+ perfomance — функциональные

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

Тестировать не нужно

- Zend Framework- Doctrine / Propel- Jquery / Prototype / Dojo- Symphony DI / Crafty / DI- all extenal libraries ...

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

Тестировать нужно

- Абстрактные классы и интефейсы- Базовые компонеты- Магическую инициализацию- Любые не очевидные вещи

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

DRY – не повторяться1 тест – 1 проверкаХороший тест – 5 строкЕсли что-то протестировано, не тетсируем это снова

Крохобор (The Nitpicker) Уклонист (The Dodger)Гигант (Giant) Любитель Порядка (The Sequencer) Счётчик (The Enumerator)

Избранный (The One) Тормоз (The Slow Poke) ...

Основные принципы

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

Class => ClassName + Test.php Namespace => TestPhpDoc => bugNumber_files — для тестовых файлов_fixtures — для фикстурphpunit.xml@cover Class::method

Организация тестов

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

<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>

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

/test/tests/application/controllers# cat IndexControllerTest.php

namespace Test;class IndexController extends \Zend\Test\PHPUnit\ControllerTestCase{ public $bootstrap = '../../bootstrap.php'; /** * @group Bug123 * @cover IndexController::indexAction */ public function testIndexAction() { $this->dispatch('/'); $this->assertController("index"); $this->assertAction("index"); }}

Тестирование Controller

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

Тестирование Controller

class IndexController extends \Zend\Test\PHPUnit\ControllerTestCase 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);

<?phpclass IndexController extends AbstractController

public function indexAction() $this->view->hello = 'Im here';

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

Тестирование Controller::init

<?php

abstract class AbstractController extends \Zend\Controller\Action{ public function init() { $this->_helper->contextSwitch() ->addActionContext('index', array('xml', 'json')) ->setAutoJsonSerialization(true) ->initContext();

$this->view->categories = new Application\Model\CategoryMapper(); }}

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

Тестирование Controller:init

class AbstractController extends \Zend\Test\PHPUnit\ControllerTestCase

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('Application\\Model\\CategoryMapper', $controller->view->categories);

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

Тестирование Form

<?phpnamespace Application\Form;class Registration extends \Zend\Form\Form 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;

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

Тестирование Form

class Registration extends \PHPUnit_Framework_TestCase public function testValidate() $form = new \Application\Form\Registration; $this->assertTrue($form->isValid(array( 'username' => 'test', 'password' => '123', 'password_retype' => '123', ))); public function testValidateFail() $form = new \Application\Form\Registration; $this->assertFalse($form->isValid(array( 'username' => 'test', 'password' => '123', 'password_retype' => '1234', )));

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

Архитектура

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

→ getConnection→ getDataSet→ createDbTableDataSet→ createDbTable→ createDbRowset

Тестирование DbTable

$categories = new \Application\Model\DbTable\Categories($this->getAdapter()); $categories->insert(array('id'=>4, 'name'=>'test')); $this->assertDataSetsEqual( $this->createFlatXmlDataSet("_fixtures/categoriesInsert.xml"), $this->createDbTableDataSet(array($categories)) );

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

Тестирование DbTable

class CategoriesTest extends \Zend\Test\PHPUnit\DatabaseTestCase

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 \Application\Model\DbTable\Categories($this->getAdapter()); $rowset = $categories->fetchAllOrdered(); $this->assertEquals(count($rowset), 3);

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

Тестирование 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>

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

Тестирование Mapper

1. Использует сервис/сервисы2. Используется моделями3. Содержит код setTable/getTable/__construct/init которые идентичны и миргурируют из одного маппера в другой

+ есть еще внешние источники данных SOAP/XML-RPC/REST/RSS

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

Тестирование Mapper

<?phpnamespace Application\Model;

class XXXMapper{ protected $_table = null;

protected $_defaultServiceName = '';

public function __construct(DbTable $table=null)

public function getTable()

public function setTable(DbTable $table) …}

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

Тестирование Mapper

Выделить общий класс AbstractMapper Выделить интерфейс для сервисов Наследовать все мапперы от абстрактного Имплементировать интерфейс Service в DbTable

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

Тестирование Mapper

<?phpnamespace Application\Model;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)}

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

Тестирование Mapper

Создаем реализации Складываем в подпапку _files в текущей директории

__Construct / Abstract class / Interface

<?phpnamespace Test;

class AbstractMapperMockAutoload extends \Application\Model\AbstractMapper{ protected $_defaultServiceName = 'Test\ServiceMock';}

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

Тестирование Mapper

class AbstractMapperTest extends \PHPUnit_Framework_TestCase

public function testInit() $mock = new AbstractMapperMockAutoload(); $this->assertType('Test\\ServiceMock', $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('Test\\ServiceMock', $mock->getService());

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

Тестирование Selenium

Selenium RC Selenium IDESelenium GRID

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

Тестирование Selenium

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

Тестирование 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')

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

Тестирование 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")); }}

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

Тестирование 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")); }

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

Тестирование 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')")); }

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

Тестирование Selenium

Вопросы ?