Upload
others
View
18
Download
0
Embed Size (px)
Citation preview
Unit and Functional Testing in SymfonyUnit and Functional Testing in Symfony//
Introduction to ReflectionIntroduction to Reflection
Shawn Biddle(shawncplus)PHP Dev, STUDIO, llc
http://www.whoisstudio.com
DO THEM!!1one!
Unit TestsUnit Tests
What Unit Tests Are ForWhat Unit Tests Are For
Revision 1: Author: shawnbfunction hello(){ return 'world';}
Revision 2: Author: jonrfunction hello(WorldStringClass $world){ return $world->name;}
shawnb was smart and wrote a unit test when he created the hello() function.
$test->is(hello(), 'world', 'hello() returns ”world”'); // PASS
jonr commits his change without running the unit test then shawnb updates and runs his test and it fails. Now shawnb can make jonr wear the dunce hat for the day because he broke the function.
$test->is(hello(), 'world', 'hello() returns ”world”'); // FAIL
What Unit Tests Are NOT ForWhat Unit Tests Are NOT For
➔ Unit tests are NOT human QA➔ Unit tests are NOT functional QA➔ Unit tests are NOT a replacement for communication
Unit tests are not an end-all replacement for someone looking over your code. They do not test implementations of objects/functions. They definitely do not remove the need for communication with other developers.
Unit Tests in SymfonyUnit Tests in Symfony
<?php include(dirname(__FILE__).'/../bootstrap/unit.php');require_once(dirname(__FILE__).'/../../lib/strtolower.php'); $t = new lime_test(7, new lime_output_color()); // strtolower()$t->diag('strtolower()');$t->isa_ok(strtolower('Foo'), 'string', 'strtolower() returns a string');$t->is(strtolower('FOO'), 'foo', 'strtolower() transforms the input to lowercase');$t->is(strtolower('foo'), 'foo', 'strtolower() leaves lowercase characters unchanged');$t->is(strtolower('12#?@~'), '12#?@~', 'strtolower() leaves non alphabetical characters unchanged');$t->is(strtolower('FOO BAR'), 'foo bar', 'strtolower() leaves blanks alone');$t->is(strtolower('FoO bAr'), 'foo bar', 'strtolower() deals with mixed case input');$t->is(strtolower(''), 'foo', 'strtolower() transforms empty strings into foo');
Unit Testing FunctionsUnit Testing Functions
Testing With FixturesTesting With Fixtures
<?php include(dirname(__FILE__).'/../bootstrap/unit.php'); require_once($sf_symfony_lib_dir.'/yaml/sfYaml.class.php');
$testCases = sfYaml::load(dirname(__FILE__).'/fixtures.yml'); $t = new lime_test(count($testCases), new lime_output_color());
// isPathAbsolute() $t->diag('isPathAbsolute()'); foreach ($testCases as $case) { $t->is(sfToolkit::isPathAbsolute($case['input']), $case['output'],$case['comment']); }
- input: '/test' output: true comment: isPathAbsolute() returns true if path is absolute- input: 'test' output: false comment: isPathAbsolute() returns false if path is relative
Works the same in Doctrine and Propel(really)
Accessing the DatabaseAccessing the Database
include(dirname(__FILE__).'/../bootstrap/unit.php');new sfDatabaseManager(ProjectConfiguration::getApplicationConfiguration('public', 'home', true));
// Test directory structuretest/ unit/ myFunctionTest.php mySecondFunctionTest.php foo/ barTest.php
> ./symfony test:unit myFunction ## Run myFunctionTest.php> ./symfony test:unit myFunction mySecondFunction ## Run both tests> ./symfony test:unit 'foo/*' ## Run barTest.php> ./symfony test:unit '*' ## Run all tests (recursive)
Putting It TogetherPutting It Together
DO THEM!!1one!
Functional TestsFunctional Tests
What Functional Tests Are ForWhat Functional Tests Are For
Short AnswerFunctional tests validate parts of your applications.
Long AnswerThey simulate a browsing session, make requests, and check elements in the response, just like you would do manually to
validate that an action does what it's supposed to do. In functional tests, you run a scenario corresponding to a use case.
What Functional Tests Are NOT ForWhat Functional Tests Are NOT For
● Functional tests are NOT human QA● Functional tests are NOT functional QA● Functional tests are NOT a replacement for communication
Functional tests are not an end-all replacement for someone looking over your code. They definitely do not remove the need for communication with other developers.
Functional Tests in SymfonyFunctional Tests in Symfony
sfTestBrowser FunctionssfTestBrowser Functions
getAndCheck($module, $action, Retrieves and checks an action $url = null, $code = 200)throwsException($class = null,
$message = null) Tests if an exception is thrown by the latest request
isResponseHeader($key, $value) Tests for a response header.
isStatusCode($statusCode = 200) Tests a status code for the current browser
call($uri, $mothod = "get", Call a request $parameters = array(), $changeStack = true)
isRedirected($boolean = true) Tests if current request has been redirected
...
See http://www.symfony-project.org/api/1_1/sfTestBrowser for the full list.
checkResponseElementcheckResponseElement
The behavior of the checkResponseElement() method depends on the type of the second argument that it receives:
➔ If it is a Boolean, it checks that an element matching the CSS selector exists.➔ If it is an integer, it checks that the CSS selector returns this number of results.➔ If it is a regular expression, it checks that the first element found by the CSS selector matches it.➔ If it is a regular expression preceded by !, it checks that the first element doesn't match the pattern.➔ For other cases, it compares the first element found by the CSS selector with the second argument as a string.
checkResponseElement ($selector, $value = true, $options = array())
checkResponseElement is incredibly powerful and can go much more in depth than this.http://www.symfony-project.org/book/1_1/15-Unit-and-Functional-Testing#Using CSS Selectors
$b = new sfTestBrowser();$b->get('/foobar/1')-> checkResponseElement('form input[type="hidden"][value="1"]', true);
// Test directory structuretest/ functional/ frontend/ myModuleActionsTest.php myScenarioTest.php backend/ myOtherScenarioTest.php
## Run all functional tests for one application, recursively> symfony test-functional frontend
## Run one given functional test> symfony test-functional frontend myScenario
## Run several tests based on a pattern> symfony test-functional frontend my*
Putting It TogetherPutting It Together
Using Lime Outside of SymfonyUsing Lime Outside of Symfony
Download/Checkout the Lime framework from: http://svn.symfony-project.com/tools/lime/trunk/lib/lime.php
require_once('/path/to/lime/lime.php');
Symfony Lime Documentation(and Cheatsheet):http://trac.symfony-project.org/wiki/LimeTestingFramework
PHP Reflection APIPHP Reflection API
Requirements➔PHP Version >= 5➔The niche need to use it
What is it?A collection of classes and one interface to ”reverse-engineer” classes, interfaces, functions and methods.
PHP Reflection APIPHP Reflection API
Classes in the Reflection API➔ReflectionException➔ReflectionFunction➔ReflectionParameter➔ReflectionClass➔ReflectionObject➔ReflectionMethod➔ReflectionProperty➔ReflectionExtension
Interfaces in the Reflection API➔Reflector
php.net/Reflection
PHP Reflection APIPHP Reflection API
class Reflection { }interface Reflector { }class ReflectionException extends Exception { }class ReflectionFunction extends ReflectionFunctionAbstract
implements Reflector { }class ReflectionParameter implements Reflector { }class ReflectionMethod extends ReflectionFunctionAbstract
implements Reflector { }class ReflectionClass implements Reflector { }class ReflectionObject extends ReflectionClass { }class ReflectionProperty implements Reflector { }class ReflectionExtension implements Reflector { }
Heirarchy
PHP Reflection APIPHP Reflection API
Recipe #1Checking for Function Documentation
$instance = new SomeClass();$refclass = new ReflectionClass($instance);$test->diag('Testing layout of '.get_class($instance));
$test->diag('Methods:');
//Check for documentationforeach($refclass->getMethods() as $method) { if($method->getDocComment() == '') { $test->output->red_bar(
sprintf('WARNING: %s does not have documentation',get_class($instance).'::'.$method->getName())
); } $doc_comment = $method->getDocComment(); if(preg_match_all('/(?<todo>TODO:.+)/', $doc_comment, $todos)) { $test->output->info(
sprintf("In method `%s`:", get_class($instance).'::'.$method->getName()));
foreach($todos['todo'] as $todo) { $test->output->info("\t".$todo); } }}
PHP Reflection APIPHP Reflection API
Recipe #2Method Structure Testing