Best Practice Testing with  Lime 2 Bernhard Schussek
Who am I? <ul><li>Web Developer since 7 years
Lead Developer at 2bePUBLISHED </li></ul><ul><ul><li>Development of symfony applications
Search Engine Marketing & Optimization
since 2007 </li></ul></ul><ul><li>Developer of  Lime 2
Recently: Symfony Core Contributor </li></ul>
Part I The Interactive Stuff
What is important? Brainstorming
Things to Keep in Mind <ul><li>Write Tests </li></ul>
Things to Keep in Mind <ul><li>Write Tests
Test Frequently! </li></ul>
Things to Keep in Mind <ul><li>Write Tests
Test Frequently!
Performance </li></ul>
Things to Keep in Mind <ul><li>Write Tests
Test Frequently!
Performance
Reliability </li><ul><li>Make sure they do actually work... </li></ul></ul>
Things to Keep in Mind <ul><li>Write Tests
Test Frequently!
Performance
Reliability </li><ul><li>Make sure they do actually work... </li></ul><li>Readability </li></ul>
Part II Testing Strategies
4 Phase Test
The Test Fixture
Test Isolation
How to Write Testable Code?
Fake Objects
Functional vs. Unit Tests
Creation and Helper Functions
Cukeet
Testing Strategies - Cukeet <ul><li>Cukeet </li></ul><ul><ul><li>Web 3.0 recipe sharing website </li></ul></ul><ul><li>Bus...
Categories
Images
... </li></ul></ul>
Testing Strategies – 4 Phase Test // @Test: resize() resizes the thumbnail copy( 'data/fixtures/test.png' ,  'web/uploads/...
Testing Strategies – 4 Phase Test // @Test: resize() resizes the thumbnail copy( 'data/fixtures/test.png' ,  'web/uploads/...
Testing Strategies – 4 Phase Test // @Test: resize() resizes the thumbnail copy( 'data/fixtures/test.png' ,  'web/uploads/...
Testing Strategies – 4 Phase Test // @Test: resize() resizes the thumbnail copy( 'data/fixtures/test.png' ,  'web/uploads/...
Testing Strategies – 4 Phase Test // @Test: resize() resizes the thumbnail copy( 'data/fixtures/test.png' ,  'web/uploads/...
Testing Strategies – 4 Phase Test <ul><li>4 Phase Test </li></ul><ul><ul><li>Fixture  setup
Test execution
Result Verification
Fixture teardown </li></ul></ul><ul><li>Goals: </li><ul><li>„ Leave the room how you entered it“
Make it obvious  what  you are testing </li></ul></ul>
Testing Strategies – 4 Phase Test // @Test: resize() resizes the thumbnail // setup copy( 'data/fixtures/test.png' ,  'web...
The Test Fixture
Testing Strategies – The Test Fixture <ul><li>Everything that a test depends on </li></ul><ul><ul><li>Data in the database...
Fresh Fixture </li></ul>
Testing Strategies – The Test Fixture Global Fixture Fresh Fixture Fixture Setup test.png, new CukeetThumbnail() Test 1 Te...
Test Isolation
Lime 2 Annotations
Testing Strategies – Annotations <ul><li>Annotations </li></ul><ul><ul><li>Control the program flow in the test </li></ul>...
@After
@BeforeAll
@AfterAll </li></ul></ul>
Testing Strategies – Annotations // @Before copy( 'data/fixtures/test.png' ,  'web/uploads/test.png' ); $thumbnail  =  new...
Testing Strategies - Annotations <ul><li>Annotations </li></ul><ul><ul><li>Improve test isolation
Fixtures can be built and destroyed automatically
The chance of interacting tests is reduced </li></ul></ul>
Testing Strategies – xUnit Style Test Class class  CukeetThumbnailTest  extends  LimeTestCase { private   $thumbnail ; pub...
How to Write Testable Code?
Testing Strategies – How to Write Testable Code class  CukeetRecipe { public   function  save() { $user  = sfContext::getI...
Testing Strategies – How to Write Testable Code class  CukeetRecipe { public   function  save() { $user  = sfContext::getI...
Expensive Operation
Unexpected Side-Effects
Lose Control
Testing Strategies – How to Write Testable Code? ? Test Tested Class Dependency
Dependency Injection
Testing Strategies – Dependency Injection <ul><li>Dependency Injection </li><ul><li>All dependencies of an object should b...
Testing Strategies – Constructor Injection class  CukeetRecipe { private   $user ; public   function  __construct(sfBasicS...
Testing Strategies – Dependency Injection // @Before $user  =  new  sfBasicSecurityUser(); $recipe  =  new  CukeetRecipe( ...
Testing Strategies – How to Write Testable Code? Test Tested Class Dependency
Testing Strategies – How to Write Testable Code? sloooowwww sfContext, Database, ... Test Tested Class Dependency
How to Replace Slow Dependencies?
Fake Objects
Testing Strategies – How to Write Testable Code? Test Tested Class Fake Object
Testing Strategies – Slow Dependencies class  CukeetRecipe { public   function  save(CukeetCategoryTable  $categoryTable )...
Testing Strategies – Fake Objects // @Test: The default category is assigned, if empty // setup $defaultCategory  =  new  ...
Testing Strategies – Fake Objects class  FakeCategoryTable  extends  CukeetCategoryTable { protected   $defaultCategory ; ...
Testing Strategies – Stub Objects <ul><li>Stub </li><ul><li>Provides fake output
Acts „as if“ it was the real object
Does not have any logic inside </li></ul></ul>
Testing Strategies – Stub Objects // @Test: The default category is assigned, if empty // setup $defaultCategory  =  new  ...
Testing Strategies – Stub Objects // @Test: The default category is assigned, if empty // setup $defaultCategory  =  new  ...
Testing Strategies – Stub Objects <ul><li>Attention! </li><ul><li>Don't replace entities (recipe, category, …)
Replace services (table, ...) </li></ul></ul>
Testing Strategies – Stub Objects Test  state after  test execution Test Tested Class Stub
Testing Strategies – Mock Objects Test  behaviour during  test execution Test Tested Class Mock
Testing Strategies – Mock Objects <ul><li>Mock </li><ul><li>Behaviour verification
Monitors indirect input
Does the tested object call the right methods? </li></ul></ul>
Testing Strategies – Mock Objects // @Test: Upon destruction all data is saved to the session // setup $storage  =  $t ->m...
Testing Strategies – Mock Objects <ul><li>Attention! </li><ul><li>Verifying behaviour leads to unflexible classes
Upcoming SlideShare
Loading in...5
×

Best Practice Testing with Lime 2

5,229

Published on

Published in: Technology, Business
1 Comment
14 Likes
Statistics
Notes
  • Excellent presentation. It represent some good practices for testing!
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total Views
5,229
On Slideshare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
162
Comments
1
Likes
14
Embeds 0
No embeds

No notes for slide

Best Practice Testing with Lime 2

  1. 1. Best Practice Testing with Lime 2 Bernhard Schussek
  2. 2. Who am I? <ul><li>Web Developer since 7 years
  3. 3. Lead Developer at 2bePUBLISHED </li></ul><ul><ul><li>Development of symfony applications
  4. 4. Search Engine Marketing & Optimization
  5. 5. since 2007 </li></ul></ul><ul><li>Developer of Lime 2
  6. 6. Recently: Symfony Core Contributor </li></ul>
  7. 7. Part I The Interactive Stuff
  8. 8. What is important? Brainstorming
  9. 9. Things to Keep in Mind <ul><li>Write Tests </li></ul>
  10. 10. Things to Keep in Mind <ul><li>Write Tests
  11. 11. Test Frequently! </li></ul>
  12. 12. Things to Keep in Mind <ul><li>Write Tests
  13. 13. Test Frequently!
  14. 14. Performance </li></ul>
  15. 15. Things to Keep in Mind <ul><li>Write Tests
  16. 16. Test Frequently!
  17. 17. Performance
  18. 18. Reliability </li><ul><li>Make sure they do actually work... </li></ul></ul>
  19. 19. Things to Keep in Mind <ul><li>Write Tests
  20. 20. Test Frequently!
  21. 21. Performance
  22. 22. Reliability </li><ul><li>Make sure they do actually work... </li></ul><li>Readability </li></ul>
  23. 23. Part II Testing Strategies
  24. 24. 4 Phase Test
  25. 25. The Test Fixture
  26. 26. Test Isolation
  27. 27. How to Write Testable Code?
  28. 28. Fake Objects
  29. 29. Functional vs. Unit Tests
  30. 30. Creation and Helper Functions
  31. 31. Cukeet
  32. 32. Testing Strategies - Cukeet <ul><li>Cukeet </li></ul><ul><ul><li>Web 3.0 recipe sharing website </li></ul></ul><ul><li>Business Model </li></ul><ul><ul><li>Recipes
  33. 33. Categories
  34. 34. Images
  35. 35. ... </li></ul></ul>
  36. 36. Testing Strategies – 4 Phase Test // @Test: resize() resizes the thumbnail copy( 'data/fixtures/test.png' , 'web/uploads/test.png' ); $thumbnail = new CukeetThumbnail( 'web/uploads/test.png' ); $thumbnail ->resize( 100 , 100 ); $size = getimagesize( $thumbnail ->getPath()); $t ->is( $size , array ( 100 , 100 ), 'The image has been resized' ); unlink( 'web/uploads/test.png' ); unset( $thumbnail ); Our Tested Class
  37. 37. Testing Strategies – 4 Phase Test // @Test: resize() resizes the thumbnail copy( 'data/fixtures/test.png' , 'web/uploads/test.png' ); $thumbnail = new CukeetThumbnail( 'web/uploads/test.png' ); $thumbnail ->resize( 100 , 100 ); $size = getimagesize( $thumbnail ->getPath()); $t ->is( $size , array ( 100 , 100 ), 'The image has been resized' ); unlink( 'web/uploads/test.png' ); unset( $thumbnail ); Fixture Setup
  38. 38. Testing Strategies – 4 Phase Test // @Test: resize() resizes the thumbnail copy( 'data/fixtures/test.png' , 'web/uploads/test.png' ); $thumbnail = new CukeetThumbnail( 'web/uploads/test.png' ); $thumbnail ->resize( 100 , 100 ); $size = getimagesize( $thumbnail ->getPath()); $t ->is( $size , array ( 100 , 100 ), 'The image has been resized' ); unlink( 'web/uploads/test.png' ); unset( $thumbnail ); Test Execution
  39. 39. Testing Strategies – 4 Phase Test // @Test: resize() resizes the thumbnail copy( 'data/fixtures/test.png' , 'web/uploads/test.png' ); $thumbnail = new CukeetThumbnail( 'web/uploads/test.png' ); $thumbnail ->resize( 100 , 100 ); $size = getimagesize( $thumbnail ->getPath()); $t ->is( $size , array ( 100 , 100 ), 'The image has been resized' ); unlink( 'web/uploads/test.png' ); unset( $thumbnail ); Result Verification
  40. 40. Testing Strategies – 4 Phase Test // @Test: resize() resizes the thumbnail copy( 'data/fixtures/test.png' , 'web/uploads/test.png' ); $thumbnail = new CukeetThumbnail( 'web/uploads/test.png' ); $thumbnail ->resize( 100 , 100 ); $size = getimagesize( $thumbnail ->getPath()); $t ->is( $size , array ( 100 , 100 ), 'The image has been resized' ); unlink( 'web/uploads/test.png' ); unset( $thumbnail ); Fixture Teardown
  41. 41. Testing Strategies – 4 Phase Test <ul><li>4 Phase Test </li></ul><ul><ul><li>Fixture setup
  42. 42. Test execution
  43. 43. Result Verification
  44. 44. Fixture teardown </li></ul></ul><ul><li>Goals: </li><ul><li>„ Leave the room how you entered it“
  45. 45. Make it obvious what you are testing </li></ul></ul>
  46. 46. Testing Strategies – 4 Phase Test // @Test: resize() resizes the thumbnail // setup copy( 'data/fixtures/test.png' , 'web/uploads/test.png' ); // test $thumbnail = new CukeetThumbnail( 'web/uploads/test.png' ); $thumbnail ->resize( 100 , 100 ); // assertions $size = getimagesize( $thumbnail ->getPath()); $t ->is( $size , array ( 100 , 100 ), 'The image has been resized' ); // teardown unlink( 'web/uploads/test.png' ); unset( $thumbnail ); Use Comments!
  47. 47. The Test Fixture
  48. 48. Testing Strategies – The Test Fixture <ul><li>Everything that a test depends on </li></ul><ul><ul><li>Data in the database, objects, files, … </li></ul></ul><ul><li>Global Fixture
  49. 49. Fresh Fixture </li></ul>
  50. 50. Testing Strategies – The Test Fixture Global Fixture Fresh Fixture Fixture Setup test.png, new CukeetThumbnail() Test 1 Test 2 Test 3 Test 4 Fixture Setup test.png, new CukeetThumbnail() Test 1 Test 2 Fixture Setup test.png, new CukeetThumbnail() Fixture Setup
  51. 51. Test Isolation
  52. 52. Lime 2 Annotations
  53. 53. Testing Strategies – Annotations <ul><li>Annotations </li></ul><ul><ul><li>Control the program flow in the test </li></ul></ul><ul><li>Supported Annotations: </li></ul><ul><ul><li>@Test </li></ul></ul><ul><ul><li>@Before
  54. 54. @After
  55. 55. @BeforeAll
  56. 56. @AfterAll </li></ul></ul>
  57. 57. Testing Strategies – Annotations // @Before copy( 'data/fixtures/test.png' , 'web/uploads/test.png' ); $thumbnail = new CukeetThumbnail( 'web/uploads/test.png' ); // @After unlink( 'web/uploads/test.png' ); unset( $thumbnail ); // @Test: resize() resizes the thumbnail $thumbnail ->resize( 100 , 100 ); $size = getimagesize( $thumbnail ->getPath()); $t ->is( $size , array ( 100 , 100 ), 'The image has been resized' );
  58. 58. Testing Strategies - Annotations <ul><li>Annotations </li></ul><ul><ul><li>Improve test isolation
  59. 59. Fixtures can be built and destroyed automatically
  60. 60. The chance of interacting tests is reduced </li></ul></ul>
  61. 61. Testing Strategies – xUnit Style Test Class class CukeetThumbnailTest extends LimeTestCase { private $thumbnail ; public function setup() { copy( 'data/fixtures/test.png' , 'web/uploads/test.png' ); $this ->thumbnail = new CukeetThumbnail( 'web/uploads/test.png' ); } public function teardown() { unlink( 'web/uploads/test.png' ); unset( $this ->thumbnail); } public function testTheThumbnailCanBeResized() {} } Same As With Annotations
  62. 62. How to Write Testable Code?
  63. 63. Testing Strategies – How to Write Testable Code class CukeetRecipe { public function save() { $user = sfContext::getInstance()->getUser(); if (! $user ->hasCredential( 'admin' )) { throw new RuntimeException( 'No permission!' ); } } }
  64. 64. Testing Strategies – How to Write Testable Code class CukeetRecipe { public function save() { $user = sfContext::getInstance()->getUser(); if (! $user ->hasCredential( 'admin' )) { throw new RuntimeException( 'No permission!' ); } } } Uh oh...
  65. 65. Expensive Operation
  66. 66. Unexpected Side-Effects
  67. 67. Lose Control
  68. 68. Testing Strategies – How to Write Testable Code? ? Test Tested Class Dependency
  69. 69. Dependency Injection
  70. 70. Testing Strategies – Dependency Injection <ul><li>Dependency Injection </li><ul><li>All dependencies of an object should be passed through method arguments </li></ul><li>Constructor Injection </li><ul><li>For required dependencies </li></ul><li>Setter Injection </li><ul><li>For optional dependencies </li></ul><li>Parameter Injection </li></ul>
  71. 71. Testing Strategies – Constructor Injection class CukeetRecipe { private $user ; public function __construct(sfBasicSecurityUser $user ) { $this ->user = $user ; } public function save() { if (! $this ->user->hasCredential( 'admin' )) { throw new RuntimeException( 'No permission!' ); } }
  72. 72. Testing Strategies – Dependency Injection // @Before $user = new sfBasicSecurityUser(); $recipe = new CukeetRecipe( $user ); // @Test: Users with credential 'admin' can save $user ->addCredential( 'admin' ); $recipe ->save( $user ); // @Test: Normal users cannot save $t ->expect( 'RuntimeException' ); $recipe ->save( $user );
  73. 73. Testing Strategies – How to Write Testable Code? Test Tested Class Dependency
  74. 74. Testing Strategies – How to Write Testable Code? sloooowwww sfContext, Database, ... Test Tested Class Dependency
  75. 75. How to Replace Slow Dependencies?
  76. 76. Fake Objects
  77. 77. Testing Strategies – How to Write Testable Code? Test Tested Class Fake Object
  78. 78. Testing Strategies – Slow Dependencies class CukeetRecipe { public function save(CukeetCategoryTable $categoryTable ) { if (is_null( $this ->Category)) { $this ->Category = $categoryTable ->findDefault(); } } }
  79. 79. Testing Strategies – Fake Objects // @Test: The default category is assigned, if empty // setup $defaultCategory = new CukeetCategory(); $categoryTable = new FakeCategoryTable( $defaultCategory ); // test $recipe = new CukeetRecipe(); $recipe ->Category = null ; $recipe ->save( $categoryTable ); // assertions $t ->is( $recipe ->Category, $defaultCategory , 'The default...' );
  80. 80. Testing Strategies – Fake Objects class FakeCategoryTable extends CukeetCategoryTable { protected $defaultCategory ; public function __construct(CukeetCategory $defaultCategory ) { $this ->defaultCategory = $defaultCategory ; } public function findDefault() { return $this ->defaultCategory; } }
  81. 81. Testing Strategies – Stub Objects <ul><li>Stub </li><ul><li>Provides fake output
  82. 82. Acts „as if“ it was the real object
  83. 83. Does not have any logic inside </li></ul></ul>
  84. 84. Testing Strategies – Stub Objects // @Test: The default category is assigned, if empty // setup $defaultCategory = new CukeetCategory(); $categoryTable = $t ->stub( 'CukeetCategoryTable' ); $categoryTable ->findDefault()->returns( $defaultCategory ); $categoryTable ->replay(); // test $recipe = new CukeetRecipe(); $recipe ->Category = null ; $recipe ->save( $categoryTable ); // assertions $t ->is( $recipe ->Category, $defaultCategory , 'The default...' ); Stub Generation in Lime 2
  85. 85. Testing Strategies – Stub Objects // @Test: The default category is assigned, if empty // setup $defaultCategory = new CukeetCategory(); $categoryTable = $t ->stub( 'CukeetCategoryTable' ); $categoryTable ->findDefault()->returns( $defaultCategory ); $categoryTable ->replay(); // test $recipe = new CukeetRecipe(); $recipe ->Category = null ; $recipe ->save( $categoryTable ); // assertions $t ->is( $recipe ->Category, $defaultCategory , 'The default...' ); Stub Configuration
  86. 86. Testing Strategies – Stub Objects <ul><li>Attention! </li><ul><li>Don't replace entities (recipe, category, …)
  87. 87. Replace services (table, ...) </li></ul></ul>
  88. 88. Testing Strategies – Stub Objects Test state after test execution Test Tested Class Stub
  89. 89. Testing Strategies – Mock Objects Test behaviour during test execution Test Tested Class Mock
  90. 90. Testing Strategies – Mock Objects <ul><li>Mock </li><ul><li>Behaviour verification
  91. 91. Monitors indirect input
  92. 92. Does the tested object call the right methods? </li></ul></ul>
  93. 93. Testing Strategies – Mock Objects // @Test: Upon destruction all data is saved to the session // setup $storage = $t ->mock( 'CukeetSessionStorage' ); $storage ->write( 'Foo' , 'Bar' ); $storage ->replay(); $user = new CukeetUser( $storage ); // test $user ->setAttribute( 'Foo' , 'Bar' ); $user ->__destruct(); // assertions $storage ->verify();
  94. 94. Testing Strategies – Mock Objects <ul><li>Attention! </li><ul><li>Verifying behaviour leads to unflexible classes
  95. 95. Implementation changes break tests
  96. 96. Overuse of mocks can harm your health </li></ul></ul>
  97. 97. Testing Strategies – Functional Tests $browser ->info( '1 - Recipes are displayed in each category' ) ->info( ' 1.1 - The two most recent recipes are displayed' ) ->click( 'Desserts' ) ->with( 'response' )->begin() ->checkElement( '#recipes li:first' , '/Deluxe Apples/' ) ->checkElement( '#recipes li:last' , '/Quatre Quarts/' ) ->checkElement( '#recipes li' , 2 ) ->end() ->click( 'Burgers' ) ->with( 'response' )->begin() ->checkElement( '#recipes li:first' , '/Kamikaze Burgers/' ) ->checkElement( '#recipes li:last' , '/Surprise Burgers/' ) ->checkElement( '#recipes li' , 2 ) ->end()
  98. 98. Functional vs. Unit Tests
  99. 99. Testing Strategies – Functional vs. Unit Tests <ul><li>Functional Tests </li></ul><ul><ul><li>„ Acceptance tests“ </li></ul></ul><ul><ul><li>„ Integration Tests“
  100. 100. Test the system as a whole
  101. 101. Collaboration between classes/components
  102. 102. Are slow! </li></ul></ul><ul><li>They do not </li></ul><ul><ul><li>Thoroughly test a system for correctness
  103. 103. Test edge cases! </li></ul></ul>
  104. 104. Testing Strategies – Functional vs. Unit Tests <ul><li>Unit Tests </li></ul><ul><ul><li>Test classes/components in isolation
  105. 105. Test edge cases
  106. 106. Are fast! </li></ul></ul><ul><li>The biggest part of an application should be tested in unit tests </li></ul>
  107. 107. Testing Strategies – Functional vs. Unit Tests $browser ->info( '1 - Recipes are displayed in each category' ) ->info( ' 1.1 - The two most recent recipes are displayed' ) ->click( 'Desserts' ) ->with( 'response' )->begin() ->checkElement( '#recipes li:first' , '/Deluxe Apples/' ) ->checkElement( '#recipes li:last' , '/Quatre Quarts/' ) ->checkElement( '#recipes li' , 2 ) ->end() ->click( 'Burgers' ) ->with( 'response' )->begin() ->checkElement( '#recipes li:first' , '/Kamikaze Burgers/' ) ->checkElement( '#recipes li:last' , '/Surprise Burgers/' ) ->checkElement( '#recipes li' , 2 ) ->end()
  108. 108. Testing Strategies – Test Edge Cases in Unit Tests // CukeetRecipeTableTest.php require_once dirname(__FILE__). '/../bootstrap/doctrine.php' ; $t ->comment( 'findRecent() returns recent recipes of a category' ); // setup ... // test $actual = $table ->findRecent( 2 , $category ); // assertions ... $t ->is( $actual , $expected , 'The correct recipes were returned' );
  109. 109. Testing Strategies – Test Edge Cases in Unit Tests // CukeetRecipeTableTest.php require_once dirname(__FILE__). '/../bootstrap/doctrine.php' ; $t ->comment( 'findRecent() returns recent recipes of a category' ); // setup ... // test $actual = $table ->findRecent( 2 , $category ); // assertions ... $t ->is( $actual , $expected , 'The correct recipes were returned' ); Bootstrap File
  110. 110. Testing Strategies – Test Edge Cases in Unit Tests // bootstrap/doctrine.php require_once dirname(__FILE__). '/unit.php' ; $database = new sfDoctrineDatabase( array ( 'name' => 'doctrine' , 'dsn' => 'sqlite::memory:' , )); // load missing model files and create tables Doctrine::createTablesFromModels(ROOT_DIR. '/lib/model' ); // load fixture data Doctrine::loadData( 'data/fixtures/fixtures.yml' ); Load YAML Fixtures
  111. 111. Testing Strategies – The Test Fixture Global Fixture Fixture Setup test.png, new CukeetThumbnail() Test 1 Test 2 Test 3 Test 4
  112. 112. Testing Strategies – Use Inline Fixtures // @Before reload(); $table = Doctrine::getTable( 'CukeetRecipe' ); // @Test: findRecent() returns recent recipes of a category // setup $category = new CukeetCategory(); $category ->fromArray( array ( 'name' => 'Desserts' , ...)); $recipe1 = new CukeetRecipe(); $recipe1 ->fromArray( array ( 'name' => 'Quatre Quarts' , ...)); $recipe1 ->Category = $category ; $recipe1 ->save(); ...
  113. 113. Creation and Helper Functions
  114. 114. Testing Strategies – Creation and Helper Functions // @Test: findRecent() returns recent recipes of a category // setup $category = createCategory(); $recipe1 = createRecipeInCategory( $category ); $recipe2 = createRecipeInCategory( $category ); $recipe3 = createRecipe(); $recipe4 = createRecipeInCategory( $category ); save( $recipe1 , $recipe2 , $recipe3 , $recipe4 ); // test $actual = $table ->findRecent( 2 , $category ); // assertions $expected = createCollection( $recipe4 , $recipe2 ); $t ->is( $actual , $expected , 'The correct recipes were returned' );
  115. 115. Testing Strategies – Creation and Helper Functions // bootstrap/doctrine.php function createRecipe( array $properties = array ()) { static $i = 0 ; $recipe = new CukeetRecipe(); $recipe ->fromArray(array_merge( array ( 'name' => 'Recipe ' . ++ $i , 'created_at' => date( 'Y-m-d H:m:i' , $i ), ), $properties )); return $recipe ; }
  116. 116. Testing Strategies – Functional Test Revisited (1) $browser ->info( '1 - Recipes are displayed in each category' ) ->info( ' 1.1 - The two most recent recipes are displayed' ) ->click( 'Desserts' ) ->with( 'response' )->begin() ->checkElement( '#recipes li:first' , '/Deluxe Apples/' ) ->checkElement( '#recipes li:last' , '/Quatre Quarts/' ) ->checkElement( '#recipes li' , 2 ) ->end() ->click( 'Burgers' ) ->with( 'response' )->begin() ->checkElement( '#recipes li:first' , '/Kamikaze Burgers/' ) ->checkElement( '#recipes li:last' , '/Surprise Burgers/' ) ->checkElement( '#recipes li' , 2 ) ->end( )
  117. 117. Testing Strategies – Functional Test Revisited (2) $browser ->info( '1 - Recipes are displayed in each category' ) ->info( ' 1.1 - The two most recent recipes are displayed' ) ->click( 'Desserts' ) ->with( 'response' )->begin() ->checkElement( '#recipes li:first' , '/Deluxe Apples/' ) ->checkElement( '#recipes li:last' , '/Quatre Quarts/' ) ->checkElement( '#recipes li' , 2 ) ->end() ->click( 'Burgers' ) ->with( 'response' )->begin() ->checkElement( '#recipes li:first' , '/Kamikaze Burgers/' ) ->checkElement( '#recipes li:last' , '/Surprise Burgers/' ) ->checkElement( '#recipes li' , 2 ) ->end()
  118. 118. Let's Summarize
  119. 119. Testing Strategies - Summary <ul><li>Su mmary </li><ul><li>4 Phase Test
  120. 120. Test Isolation
  121. 121. Annotations
  122. 122. Dependency Injection
  123. 123. Stubs
  124. 124. Mocks
  125. 125. Functional vs. Unit Tests
  126. 126. Creation and Helper Functions </li></ul></ul>
  127. 127. Part III Whatz Nu in Lime 2
  128. 128. Annotation Support
  129. 129. What Nu in Lime 2 – Annotation Support // @Before copy( 'data/fixtures/test.png' , 'web/uploads/test.png' ); $thumbnail = new CukeetThumbnail( 'web/uploads/test.png' ); // @After unlink( 'web/uploads/test.png' ); unset( $thumbnail ); // @Test: resize() resizes the thumbnail $thumbnail ->resize( 100 , 100 ); $size = getimagesize( $thumbnail ->getPath()); $t ->is( $size , array ( 100 , 100 ), 'The image has been resized' );
  130. 130. Stub & Mock Generation
  131. 131. Whatz Nu in Lime 2 – Mock Objects // @Test: Upon destruction all data is saved to the session // setup $storage = $t ->mock( 'SessionStorage' ); $storage ->write( 'Foo' , 'Bar' )->returns( true )->once(); $storage ->flush()->atLeastOnce(); $storage ->any( 'read' )->throws( 'BadMethodCallException' ); $storage ->replay(); // test ...
  132. 132. Improved Error Messages
  133. 133. What Nu in Lime 2 – Improved Error Messages # got: object(CukeetRecipe) ( # ... # '_data' => array ( # ... # 'name' => 'Quatre Quarts', # ... # ), # ) # expected: object(CukeetRecipe) ( # ... # '_data' => array ( # ... # 'name' => 'Kamikaze Burger', # ... # ), # )
  134. 134. Multi-Processing Support
  135. 135. Whatz Nu in Lime 2 – Multi-Processing Support <ul><li>Test Suite in 1 Process </li><ul><li>Total 3:20 minutes
  136. 136. One CPU core at 100%
  137. 137. One CPU core at 20% </li></ul><li>Test Suite in 8 Processes </li><ul><li>Total 1:40 minutes
  138. 138. Both CPU cores at 100% </li></ul><li>Up To 100% Faster! </li></ul>
  139. 139. Extensibility
  140. 140. Whatz Nu in Lime 2 - Extensibility <ul><li>Extensible Testers </li><ul><li>override is(), like() etc. for specific types </li></ul></ul>class LimeTesterException extends LimeTesterObject { // don't compare stack trace protected $properties = array ( 'message' , 'code' , 'file' , 'line' ); } LimeTester::register( 'Exception' , 'LimeTesterException' ); $exception1 = new RuntimeException( 'Foobar' , 10 ); $exception2 = new RuntimeException( 'Foobar' , 10 ); $t ->is( $exception1 , $exception2 , 'Hurray!' )
  141. 141. Whatz Nu in Lime 2 - Extensibility <ul><li>Extensible Outputs </li><ul><li>Implement custom outputs for the tests
  142. 142. Log-Output
  143. 143. Email-Output
  144. 144. ... </li></ul></ul>
  145. 145. Integration With CI-Tools sismo php Under Control
  146. 146. ?
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×