Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Test-Driven Development for TYPO3

2,482 views

Published on

Published in: Technology, Business
  • Be the first to comment

Test-Driven Development for TYPO3

  1. 1. Test-Driven Developmentfor TYPO3Oliver Klee, 2011-12-13 and 2011-12-14typo3-coding@oliverklee.de
  2. 2. Unittests?
  3. 3. Manual testing is cumbersome
  4. 4. Automatedtesting isfast
  5. 5. Unit tests for the Realty Manager extension
  6. 6. more than1600 tests
  7. 7. in less than 60 seconds
  8. 8. Verify that your codedoes what you expect
  9. 9. Make sure that yourchanges won‘t break anything
  10. 10. Keep other codersfrom breaking your code
  11. 11. Don‘t break anythingeven in complex projects
  12. 12. Create asafety net for refactoring
  13. 13. Green feels good!
  14. 14. Green feels good!
  15. 15. Know your tools
  16. 16. The phpunit extension has it all
  17. 17. The phpunit extension has it allphpunit (TYPO3 extension)
  18. 18. The phpunit extension has it allphpunit (TYPO3 extension) PHPUnit (PEAR package)
  19. 19. The phpunit extension has it allphpunit (TYPO3 extension) PHPUnit (PEAR package) Testrunner (back-end- module)
  20. 20. The phpunit extension has it allphpunit (TYPO3 extension) PHPUnit (PEAR package) Testrunner Testrunner (back-end- module) (CLI module)
  21. 21. The phpunit extension has it allphpunit (TYPO3 extension) PHPUnit (PEAR package) Testing framework for FE & DB Testrunner Testrunner (back-end- module) (CLI module)
  22. 22. Let‘s getsome terms straight
  23. 23. Two testsmeet in a bar ...
  24. 24. Two testsTest meet in a bar ...
  25. 25. Two tests Test meet in aAssertion bar ...
  26. 26. Two tests Test meet in aAssertion bar ... Test Test
  27. 27. Two tests Test meet in aAssertion bar ... Test TestTest case
  28. 28. Two tests Test meet in aAssertion bar ... Test TestTest caseTest case
  29. 29. Two tests Test meet in aAssertion bar ... Test TestTest caseTest caseTest suite
  30. 30. Code test-first
  31. 31. Code test-first
  32. 32. Code test-firstwritetest
  33. 33. Code test-firstwritetest
  34. 34. Code test-firstwrite writetest code
  35. 35. Code test-firstwrite writetest code
  36. 36. Code test-firstwrite writetest code
  37. 37. Code test-firstwrite writetest code refactor
  38. 38. Code test-firstwrite writetest code refactor
  39. 39. The life cycle unit test of a
  40. 40. The life cycle unit test of a
  41. 41. The life cycle unit test of a new FooTest();
  42. 42. The life cycle unit test of a new FooTest();
  43. 43. The life cycle unit test of a new FooTest(); setUp();
  44. 44. The life cycle unit test of a new FooTest(); setUp(); /** @test */ lifeIsGood();
  45. 45. The life cycle unit test of a new FooTest(); setUp(); /** @test */ lifeIsGood();
  46. 46. The life cycle unit test of a new FooTest(); setUp(); /** @test */ lifeIsGood(); tearDown();
  47. 47. The life cycle unit test of a new FooTest(); setUp(); /** @test */ lifeIsGood(); tearDown();
  48. 48. The life cycle unit test of a new FooTest(); setUp(); /** @test */ lifeIsGood(); tearDown();
  49. 49. Use meaningful unit test names
  50. 50. Use meaningful unit test namesName thebehavior. classCanBeInstantiated
  51. 51. Use meaningful unit test names Name the behavior. classCanBeInstantiatedMention the method. setTitleSetsTitle
  52. 52. Use meaningful unit test names Name the behavior. classCanBeInstantiatedMention the method. setTitleSetsTitle Name the setSizeWithZeroThrowsExceptionpreconditions. hasTitleForEmptyTitleReturnsFalse
  53. 53. Use meaningful unit test names Name the behavior. classCanBeInstantiatedMention the method. setTitleSetsTitle Name the setSizeWithZeroThrowsExceptionpreconditions. hasTitleForEmptyTitleReturnsFalse Dont‘t use "works" or "correctly".
  54. 54. Use meaningful unit test names Name the behavior. classCanBeInstantiatedMention the method. setTitleSetsTitle Name the setSizeWithZeroThrowsExceptionpreconditions. hasTitleForEmptyTitleReturnsFalse Dont‘t use "works" or measureFrubbleWorksCorrectly "correctly".
  55. 55. Use meaningful unit test names Name the behavior. classCanBeInstantiatedMention the method. setTitleSetsTitle Name the setSizeWithZeroThrowsExceptionpreconditions. hasTitleForEmptyTitleReturnsFalse Dont‘t use "works" or measureFrubbleWorksCorrectly "correctly".
  56. 56. ContinuousIntegration
  57. 57. ContinuousIntegration =
  58. 58. ContinuousIntegration =checking inmore often
  59. 59. The continuous integration server monitors the code Checkin Repository (SVN)
  60. 60. The continuous integration server monitors the code Checkin hook Repository (SVN) SVN
  61. 61. The continuous integration server monitors the code Checkin hook Repository (SVN) SVN continuous integration server
  62. 62. The continuous integration server monitors the code Checkin hook Repository (SVN) SVN Hey, a change! continuous integration server
  63. 63. The continuous integration server monitors the code Checkin hook Repository (SVN) SVN Hey, a change! continuous integration run the unit server tests etc.
  64. 64. The continuous integration server monitors the code Checkin hook Repository (SVN) SVN Hey, a change! continuous integration run the unit Report server tests etc.
  65. 65. There are small and huge tests
  66. 66. Unit tests are small and fast
  67. 67. Integration tests test the complete thing
  68. 68. Automated click tests test the interface
  69. 69. Automated click tests test the interface Selenium
  70. 70. Blackbox tests test the public interface
  71. 71. Whitebox tests test the inner workings
  72. 72. The testing framework is created quickly /** * @var Tx_Phpunit_Framework */ protected $testingFramework = NULL; public function setUp() { $this->testingFramework = new Tx_Phpunit_Framework(tx_news2); } public function tearDown() { $this->testingFramework->cleanUp(); unset($this->testingFramework); }
  73. 73. The testing framework is created quickly /** * @var Tx_Phpunit_Framework */ protected $testingFramework = NULL; public function setUp() { $this->testingFramework = new Tx_Phpunit_Framework(tx_news2); } discard the FE, public function tearDown() { delete DB records, $this->testingFramework->cleanUp(); unset($this->testingFramework); delete files }
  74. 74. The testing framework is created quickly /** * @var Tx_Phpunit_Framework */ protected $testingFramework = NULL; public function setUp() { $this->testingFramework = new Tx_Phpunit_Framework(tx_news2); } discard the FE, public function tearDown() { delete DB records, $this->testingFramework->cleanUp(); unset($this->testingFramework); delete files }CREATE TABLE tx_news2_domain_model_news ( … is_dummy_record tinyint(1) unsigned DEFAULT 0 NOT NULL, …
  75. 75. The testing framework can fake almost everything
  76. 76. The testing framework can fake almost everything $recordUid = $tf->createRecord($tableName, array $recordData = array());
  77. 77. The testing framework can fake almost everything $recordUid = $tf->createRecord($tableName, array $recordData = array()); $tf->changeRecord($tableName, $uid, array $recordData);
  78. 78. The testing framework can fake almost everything $recordUid = $tf->createRecord($tableName, array $recordData = array()); $tf->changeRecord($tableName, $uid, array $recordData); $tf->deleteRecord($tableName, $uid);
  79. 79. The testing framework can fake almost everything $recordUid = $tf->createRecord($tableName, array $recordData = array()); $tf->changeRecord($tableName, $uid, array $recordData); $tf->deleteRecord($tableName, $uid); $tf->createRelation($tableName, $uidLocal, $uidForeign, $sorting = 0);
  80. 80. The testing framework can fake almost everything $recordUid = $tf->createRecord($tableName, array $recordData = array()); $tf->changeRecord($tableName, $uid, array $recordData); $tf->deleteRecord($tableName, $uid); $tf->createRelation($tableName, $uidLocal, $uidForeign, $sorting = 0);$tf->createRelationAndUpdateCounter($tableName, $uidLocal, $uidForeign, $columnName);
  81. 81. The testing framework can fake almost everything $recordUid = $tf->createRecord($tableName, array $recordData = array()); $tf->changeRecord($tableName, $uid, array $recordData); $tf->deleteRecord($tableName, $uid); $tf->createRelation($tableName, $uidLocal, $uidForeign, $sorting = 0);$tf->createRelationAndUpdateCounter($tableName, $uidLocal, $uidForeign, $columnName); $tf->removeRelation($tableName, $uidLocal, $uidForeign);
  82. 82. The testing framework can fake almost everything $recordUid = $tf->createRecord($tableName, array $recordData = array()); $tf->changeRecord($tableName, $uid, array $recordData); $tf->deleteRecord($tableName, $uid); $tf->createRelation($tableName, $uidLocal, $uidForeign, $sorting = 0);$tf->createRelationAndUpdateCounter($tableName, $uidLocal, $uidForeign, $columnName); $tf->removeRelation($tableName, $uidLocal, $uidForeign); $numberOfRecords = $tf->countRecords($tableName, $whereClause = );
  83. 83. The testing framework can fake almost everything $recordUid = $tf->createRecord($tableName, array $recordData = array()); $tf->changeRecord($tableName, $uid, array $recordData); $tf->deleteRecord($tableName, $uid); $tf->createRelation($tableName, $uidLocal, $uidForeign, $sorting = 0);$tf->createRelationAndUpdateCounter($tableName, $uidLocal, $uidForeign, $columnName); $tf->removeRelation($tableName, $uidLocal, $uidForeign); $numberOfRecords = $tf->countRecords($tableName, $whereClause = ); $success = $tf->existsRecord($tableName, $whereClause = );
  84. 84. The testing framework can fake almost everything $recordUid = $tf->createRecord($tableName, array $recordData = array()); $tf->changeRecord($tableName, $uid, array $recordData); $tf->deleteRecord($tableName, $uid); $tf->createRelation($tableName, $uidLocal, $uidForeign, $sorting = 0);$tf->createRelationAndUpdateCounter($tableName, $uidLocal, $uidForeign, $columnName); $tf->removeRelation($tableName, $uidLocal, $uidForeign); $numberOfRecords = $tf->countRecords($tableName, $whereClause = ); $success = $tf->existsRecord($tableName, $whereClause = ); $success = $tf->existsExactlyOneRecord($tableName, $whereClause = );
  85. 85. The testing framework can fake almost everything $recordUid = $tf->createRecord($tableName, array $recordData = array()); $tf->changeRecord($tableName, $uid, array $recordData); $tf->deleteRecord($tableName, $uid); $tf->createRelation($tableName, $uidLocal, $uidForeign, $sorting = 0);$tf->createRelationAndUpdateCounter($tableName, $uidLocal, $uidForeign, $columnName); $tf->removeRelation($tableName, $uidLocal, $uidForeign); $numberOfRecords = $tf->countRecords($tableName, $whereClause = ); $success = $tf->existsRecord($tableName, $whereClause = ); $success = $tf->existsExactlyOneRecord($tableName, $whereClause = ); $success = $tf->existsRecordWithUid($tableName, $uid);
  86. 86. The testing framework can fake almost everything
  87. 87. The testing framework can fake almost everything $pageUid = $tf->createFrontEndPage($parentId = 0, array $recordData = array());
  88. 88. The testing framework can fake almost everything $pageUid = $tf->createFrontEndPage($parentId = 0, array $recordData = array()); $pageUid = $tf->createSystemFolder($parentId = 0, array $recordData = array());
  89. 89. The testing framework can fake almost everything $pageUid = $tf->createFrontEndPage($parentId = 0, array $recordData = array()); $pageUid = $tf->createSystemFolder($parentId = 0, array $recordData = array()); $elementUid = $tf->createContentElement($pageId = 0, array $recordData = array());
  90. 90. The testing framework can fake almost everything $pageUid = $tf->createFrontEndPage($parentId = 0, array $recordData = array()); $pageUid = $tf->createSystemFolder($parentId = 0, array $recordData = array()); $elementUid = $tf->createContentElement($pageId = 0, array $recordData = array()); $tf->createTemplate($pageId, array $recordData = array());
  91. 91. The testing framework can fake almost everything $pageUid = $tf->createFrontEndPage($parentId = 0, array $recordData = array()); $pageUid = $tf->createSystemFolder($parentId = 0, array $recordData = array()); $elementUid = $tf->createContentElement($pageId = 0, array $recordData = array()); $tf->createTemplate($pageId, array $recordData = array()); $pageUid = $tf->createFakeFrontEnd($pageUid = 0);
  92. 92. The testing framework can fake almost everything $pageUid = $tf->createFrontEndPage($parentId = 0, array $recordData = array()); $pageUid = $tf->createSystemFolder($parentId = 0, array $recordData = array()); $elementUid = $tf->createContentElement($pageId = 0, array $recordData = array()); $tf->createTemplate($pageId, array $recordData = array()); $pageUid = $tf->createFakeFrontEnd($pageUid = 0); $tf->discardFakeFrontEnd();
  93. 93. The testing framework can fake almost everything $pageUid = $tf->createFrontEndPage($parentId = 0, array $recordData = array()); $pageUid = $tf->createSystemFolder($parentId = 0, array $recordData = array()); $elementUid = $tf->createContentElement($pageId = 0, array $recordData = array()); $tf->createTemplate($pageId, array $recordData = array()); $pageUid = $tf->createFakeFrontEnd($pageUid = 0); $tf->discardFakeFrontEnd(); $groupUid = $tf->createFrontEndUserGroup(array $recordData = array());
  94. 94. The testing framework can fake almost everything $pageUid = $tf->createFrontEndPage($parentId = 0, array $recordData = array()); $pageUid = $tf->createSystemFolder($parentId = 0, array $recordData = array()); $elementUid = $tf->createContentElement($pageId = 0, array $recordData = array()); $tf->createTemplate($pageId, array $recordData = array()); $pageUid = $tf->createFakeFrontEnd($pageUid = 0); $tf->discardFakeFrontEnd(); $groupUid = $tf->createFrontEndUserGroup(array $recordData = array()); $userUid = $tf->createFrontEndUser($groups = , array $recordData = array());
  95. 95. The testing framework can fake almost everything $pageUid = $tf->createFrontEndPage($parentId = 0, array $recordData = array()); $pageUid = $tf->createSystemFolder($parentId = 0, array $recordData = array()); $elementUid = $tf->createContentElement($pageId = 0, array $recordData = array()); $tf->createTemplate($pageId, array $recordData = array()); $pageUid = $tf->createFakeFrontEnd($pageUid = 0); $tf->discardFakeFrontEnd(); $groupUid = $tf->createFrontEndUserGroup(array $recordData = array()); $userUid = $tf->createFrontEndUser($groups = , array $recordData = array()); $tf->loginFrontEndUser($userId);
  96. 96. The testing framework can fake almost everything $pageUid = $tf->createFrontEndPage($parentId = 0, array $recordData = array()); $pageUid = $tf->createSystemFolder($parentId = 0, array $recordData = array()); $elementUid = $tf->createContentElement($pageId = 0, array $recordData = array()); $tf->createTemplate($pageId, array $recordData = array()); $pageUid = $tf->createFakeFrontEnd($pageUid = 0); $tf->discardFakeFrontEnd(); $groupUid = $tf->createFrontEndUserGroup(array $recordData = array()); $userUid = $tf->createFrontEndUser($groups = , array $recordData = array()); $tf->logoutFrontEndUser(); $tf->loginFrontEndUser($userId);
  97. 97. The testing framework can fake almost everything $pageUid = $tf->createFrontEndPage($parentId = 0, array $recordData = array()); $pageUid = $tf->createSystemFolder($parentId = 0, array $recordData = array()); $elementUid = $tf->createContentElement($pageId = 0, array $recordData = array()); $tf->createTemplate($pageId, array $recordData = array()); $pageUid = $tf->createFakeFrontEnd($pageUid = 0); $tf->discardFakeFrontEnd(); $groupUid = $tf->createFrontEndUserGroup(array $recordData = array()); $userUid = $tf->createFrontEndUser($groups = , array $recordData = array()); $tf->logoutFrontEndUser(); $tf->loginFrontEndUser($userId);$userUid = $tf->createAndLoginFrontEndUser($groups = , array $recordData = array());
  98. 98. The testing framework can fake almost everything $pageUid = $tf->createFrontEndPage($parentId = 0, array $recordData = array()); $pageUid = $tf->createSystemFolder($parentId = 0, array $recordData = array()); $elementUid = $tf->createContentElement($pageId = 0, array $recordData = array()); $tf->createTemplate($pageId, array $recordData = array()); $pageUid = $tf->createFakeFrontEnd($pageUid = 0); $tf->discardFakeFrontEnd(); $groupUid = $tf->createFrontEndUserGroup(array $recordData = array()); $userUid = $tf->createFrontEndUser($groups = , array $recordData = array()); $tf->logoutFrontEndUser(); $tf->loginFrontEndUser($userId);$userUid = $tf->createAndLoginFrontEndUser($groups = , array $recordData = array()); $isLoggedIn = $tf->isLoggedIn();
  99. 99. The testing framework can fake almost everything
  100. 100. The testing framework can fake almost everything $recordUid = $tf->createBackEndUser(array $recordData = array());
  101. 101. The testing framework can fake almost everything $recordUid = $tf->createBackEndUser(array $recordData = array()); $recordUid = $tf->createBackEndUserGroup(array $recordData = array());
  102. 102. The testing framework can fake almost everything
  103. 103. The testing framework can fake almost everything $path = $tf->createDummyFile($fileName = test.txt, $content = );
  104. 104. The testing framework can fake almost everything $path = $tf->createDummyFile($fileName = test.txt, $content = );$path = $tf->createDummyZipArchive($fileName = test.zip, array $filesToAdd = array());
  105. 105. The testing framework can fake almost everything $path = $tf->createDummyFile($fileName = test.txt, $content = );$path = $tf->createDummyZipArchive($fileName = test.zip, array $filesToAdd = array()); $tf->deleteDummyFile($fileName);
  106. 106. The testing framework can fake almost everything $path = $tf->createDummyFile($fileName = test.txt, $content = );$path = $tf->createDummyZipArchive($fileName = test.zip, array $filesToAdd = array()); $tf->deleteDummyFile($fileName); $path = $tf->createDummyFolder($folderName);
  107. 107. The testing framework can fake almost everything $path = $tf->createDummyFile($fileName = test.txt, $content = );$path = $tf->createDummyZipArchive($fileName = test.zip, array $filesToAdd = array()); $tf->deleteDummyFile($fileName); $path = $tf->createDummyFolder($folderName); $tf->deleteDummyFolder($folderName);
  108. 108. How to your test extbase controllersclass Tx_Coffee_Controller_CoffeeControllerTest extends Tx_Extbase_Tests_Unit_BaseTestCase { /** @var Tx_Coffee_Controller_CoffeeController */ protected $fixture; /** @var Tx_Fluid_View_TemplateView */ protected $view = NULL; /** @var Tx_Coffee_Domain_Repository_CoffeeRepository */ protected $coffeeRepository = NULL; public function setUp() { $this->fixture = new Tx_Coffee_Controller_CoffeeController(); $this->view = $this->getMock(Tx_Fluid_View_TemplateView, array(), array(), , FALSE); $this->fixture->setView($this->view); $this->objectManager = $this->getMock(Tx_Extbase_Object_ObjectManagerInterface); $this->fixture->injectObjectManager($this->objectManager); $this->coffeeRepository = $this->getMock( Tx_Coffee_Domain_Repository_CoffeeRepository, array(findAll, findByBrand), array($this->objectManager) ); $this->fixture->injectCoffeeRepository($this->coffeeRepository); }
  109. 109. How to your test extbase controllersclass Tx_Coffee_Controller_CoffeeControllerTest extends Tx_Extbase_Tests_Unit_BaseTestCase { /** @var Tx_Coffee_Controller_CoffeeController */ protected $fixture; /** @var Tx_Fluid_View_TemplateView */ protected $view = NULL; /** @var Tx_Coffee_Domain_Repository_CoffeeRepository */ protected $coffeeRepository = NULL; public function setUp() { $this->fixture = new Tx_Coffee_Controller_CoffeeController(); $this->view = $this->getMock(Tx_Fluid_View_TemplateView, array(), array(), , FALSE); $this->fixture->setView($this->view); $this->objectManager = $this->getMock(Tx_Extbase_Object_ObjectManagerInterface); $this->fixture->injectObjectManager($this->objectManager); $this->coffeeRepository = $this->getMock( Tx_Coffee_Domain_Repository_CoffeeRepository, array(findAll, findByBrand), array($this->objectManager) ); $this->fixture->injectCoffeeRepository($this->coffeeRepository); } class Tx_Coffee_Controller_CoffeeController extends Tx_Extbase_MVC_Controller_ActionController { /** * Sets the view. * * This function is intended to be used for unit testing purposes only. * * @param Tx_Fluid_View_TemplateView $view the new view * * @return void */ public function setView(Tx_Fluid_View_TemplateView $view) { $this->view = $view; }
  110. 110. How to test your extbase controllers
  111. 111. How to your test extbase controllers/** * @test */public function indexActionAssignsAllCoffeesToView() { $models = $this->getMock(Tx_Extbase_Persistence_QueryResultInterface); $this->coffeeRepository->expects($this->once())->method(findAll) ->will($this->returnValue($models)); $this->view->expects($this->at(0))->method(assign)->with(coffees, $models); $this->fixture->indexAction();}
  112. 112. How to your test extbase controllers/** * @test */public function indexActionAssignsAllCoffeesToView() { $models = $this->getMock(Tx_Extbase_Persistence_QueryResultInterface); $this->coffeeRepository->expects($this->once())->method(findAll) ->will($this->returnValue($models)); $this->view->expects($this->at(0))->method(assign)->with(coffees, $models); $this->fixture->indexAction();}/** * @test */public function showActionAssignsModelToView() { $model = clone new Tx_Coffee_Domain_Model_Coffee(); $this->view->expects($this->at(0))->method(assign)->with(coffee, $model); $this->fixture->showAction($model);}

×