PHP unit testing + new PHPUnit patch for type testing functionality
Seems bullet points are not working and some of the slides are not so clear because of Slideshare conversion.
Presentation given at phpBenelux meeting August 25, 2010
Report
Share
Report
Share
1 of 51
Download to read offline
More Related Content
From typing the test to testing the type
1. From typing the test to testing the type Wim Godden Cu.be Solutions User meeting Aug 25, 2010
20. a typical unit testing presentation (as you'll notice...)
21. -> Plenty of online resources, tutorials, conference talks, etc. about the regular stuff
22. What's unit testing ? <Wikipedia> Unit testing is a software design and development method where the programmer gains confidence that individual units of source code are fit for use. </Wikipedia>
23. What's unit testing ? <Wikipedia> Unit testing is a software design and development method where the programmer gains confidence that individual units of source code are fit for use. </Wikipedia>
24. What's a unit ? <?php class Fridge { protected $beerBottles ; public function getBeerSupply() { return $this ->beerBottles; } public function addBeer( $bottles ) { $this ->beerBottles += $bottles ; return true ; } public function drinkBeer( $bottles ) { $this ->beerBottles -= $bottles ; return true ; } }
98. Good tests... public function testSetAgeActuallySets() { $newAge = mt_rand( 0 , 150 ); $this ->User->setAge( $newAge ); $this ->assertEquals( $newAge , $this ->User->getAge() ); } Good tests don't just cover code... they test the functionality of the code !
99. Testing code – not always easy Some things are hard to test : Private methods
108. Make a testObject array protected $testObjects; public function testInvoice() { $billingAddress = new Address("Meir 12", 2000, "Antwerpen", "BE"); $this->addTestObject($billingAddress); $officeAddress = new Address("Groenplaats 50", 2000, "Antwerpen", "BE"); $this->addTestObject($officeAddress); $customer = new Customer(402, "Jan", "De Man", 32, $billingAddress, $officeAddress); $this->addTestObject($customer); $product = new Product(92, "Speculaas", 5.95); $this->addTestObject($product); $invoice = new Invoice($customer); $this->addTestObject($invoice); $invoice.addItemQuantity($product, 5); $lineItems = $invoice.getLineitems(); $this->assertEquals(1, $lineItems.size()); $actualLineItem = $lineItems.get(0); $this->assertEquals($invoice, $actualLineItem.getInvoice()); $this->assertEquals($product, $actualLineItem.getProduct()); $this->assertEquals($quantity, $actualLineItem, getQuantity()); $this->assertEquals(5.95, $actualLineItem.getUnitPrice()); } public function tearDown() { $this->deleteTestObjects(); } Note : this is a design decision from the very start of your project, since all objects need the same method for deletion
109. Replace the unneeded data protected $testObjects; public function testInvoice() { $billingAddress = new TestAddress(); $this->addTestObject($billingAddress); $officeAddress = new TestAddress(); $this->addTestObject($officeAddress); $customer = new TestCustomer($billingAddress, $officeAddress); $this->addTestObject($customer); $product = new Product(92, "Speculaas", 5.95); $this->addTestObject($product); $invoice = new Invoice($customer); $this->addTestObject($invoice); $invoice.addItemQuantity($product, 5); $lineItems = $invoice.getLineitems(); $this->assertEquals(1, $lineItems.size()); $actualLineItem = $lineItems.get(0); $this->assertEquals($invoice, $actualLineItem.getInvoice()); $this->assertEquals($product, $actualLineItem.getProduct()); $this->assertEquals($quantity, $actualLineItem, getQuantity()); $this->assertEquals(5.95, $actualLineItem.getUnitPrice()); } public function tearDown() { $this->deleteTestObjects(); }
110. Instantiating objects (1/3) Looks like a standard test-class relationship, with invoice being a 'leaf' class
135. When the method is called from our test, it compares the type of each parameter with the type in the docblock :
136. The result : /** * Cat constructor * @param string $name The name of the cat * @param int $lives The number of lives this cat has left */ PHPUnit 3.5 by Sebastian Bergmann. FF... Time: 1 second, Memory: 3.25Mb There were 2 failures: 1) CatTest::test__constructWithNegativeLives Invalid type calling Cat->__construct : parameter 1 ($name) should be of type string but got bool instead in /home/dev/paramtest/tests/CatTest.php 2) CatTest::test__constructWithNegativeLives Invalid type calling Cat->__construct : parameter 2 ($lives) should be of type int but got string instead in /home/dev/paramtest/tests/CatTest.php FAILURES! Tests: 5, Assertions: 8, Failures: 2. $cat = new Cat(true, "9");
137. Why it's useful PHP's dynamic typing is great, but it can be a pain !
138. If you write a library : You want to be sure people pass the right parameter types
139. You don't want to do is_int(), is_bool(), ... all the time If you modify your function's parameters : If you forget to modify the callee : warning will be shown
140. If you forget to update your docblock : warning will be shown Parameter type validation was designed to help your data be consistent and of the type expected during design !
It's not the responsibility of the class to create the new classes, it's the responsibility of the callee to provide these objects. For example : an object using a cache should not instantiate the cache, nor do cache::getInstance, instead the cache object should be provided to the object.