FUNCTIONAL TESTING(overview)
Functional tests validate parts of your applications.
Functional tests simulate a browsing session to assert desired user outcomes. They automate requests and check elements in the response. It is advantageous to write your functional tests to correspond to individual user stories.
User Story:As an Administrator I want to create a company so I can manage companies
Wednesday, January 6, 2010
WHY WRITE THEM?
-BETTER TEST COVERAGE!!
- Cover areas unit tests can’t reach
- Unit Tests: Model Layer
- Functional Tests: View and Control Layers
- Mimic Functional QA
- Write tests according to client-approved user
stories.
Wednesday, January 6, 2010
TOOLS FOR FUNCTIONAL TESTING
Cucumber
“Cucumber is designed to allow you to execute feature documentation
written in plain text (often known as ‘stories’).”
Selenium“Selenium is written in JavaScript; it's designed to run in a real browser to
test compatibility with a real browser”
•Uses a real browser
•Writes test in JavaScript, can play back through any browser
•We will discuss this later
Wednesday, January 6, 2010
-Bootstrap Symfony Core
-sfBrowser
-Symfony class simulating a true browser
-Testing Blocks
-sfTestFunctional split into multiple classes- sfTesterResponse, sfTesterRequest, sfTesterDoctrine, sfTesterUser, sfTesterForm
-Extend the functional test framework for
custom functionality
FUNCTIONAL TESTING IN SYMFONY
Wednesday, January 6, 2010
EXTEND THE TEST FRAMEWORKSubclass sfTestFunctional
class csTestFunctional extends sfTestFunctional{ public function clickAndCheck($link, $module, $action, $statusCode = 200) { return $this->click($link)->isModuleAction($module, $action, $statusCode); }
public function isModuleAction($module, $action, $statusCode = 200) { $this->with('request')->begin()-> isParameter('module', $module)-> isParameter('action', $action)-> end()->
with('response')->begin()-> isStatusCode($statusCode)-> end();
return $this; }}
Wednesday, January 6, 2010
USE FIXTURES// test/data/urls.yml- url: /blog module: blog action: index statusCode: 200- url: /blog/new module: blog action: new statusCode: 200- url: /blog/edit module: blog action: edit statusCode: 401
// test/functional/frontend/routeTest.php foreach(sfYaml::load(sfConfig::get('sf_test_dir').'/data/forms.yml') as $route) { $browser ->get($route['url']) ->isModuleAction($route['module'], $route['action'], $route['statusCode']) ; }
Simplify Repetitive Tests!
Create a YAML file to hold data
AND...
Wednesday, January 6, 2010
CREATE YOUR OWN TESTERExtend sfTesterForm
class csTesterForm extends sfTesterForm{ // Called at every occurence of $browser->with('form') public function initialize() { // Load form into local variable parent::initialize(); } // Called when a page is requested // (at every occurence of $browser->call()) public function prepare() { } public function fill($name, $values = array()) { $formValues = Doctrine_Lib::arrayDeepMerge($this->getFormValues($name), $values);
foreach ($formValues as $key => $value) { $this->setDefaultField($key, $value); }
return $this->getObjectToReturn(); }}
Wednesday, January 6, 2010
// test/data/forms.ymllogin_bad: signin: username: admin password: wrongpassword
login: signin: username: admin password: rightpassword login2: signin[username]: admin signin[password]: rightpassword
// lib/test/csTesterForm.class.phppublic function __construct(sfTestFunctionalBase $browser, $tester){ parent::__construct($browser, $tester); if (file_exists(sfConfig::get('sf_test_dir').'/data/forms.yml')) { $this->setFormData(sfYaml::load(sfConfig::get('sf_test_dir').'/data/forms.yml')); }}
Extend sfTesterForm (continued)
Create Form Fixtures
Load them into your Tester!
// test/functional/backend/loginTest.php$browser = new csTestFunctional(new sfBrowser(), null, array('form' => 'csTesterForm'));
Instantiate Your Classes
Wednesday, January 6, 2010
USE FACTORIESHelpful classes to generate objects and data
class sfFactory{ protected static $lastRandom = null; public static function generate($prefix = '') { self::$lastRandom = $prefix.rand(); return self::$lastRandom; } public static function last() { if (!self::$lastRandom) { throw new sfException("No previously generated random available"); } return self::$lastRandom; }}
Wednesday, January 6, 2010
PUTTING IT ALL TOGETHER
$browser = new csTestFunctional(new sfBrowser(), null, array('form' => 'csTesterForm', 'doctrine' => 'sfTesterDoctrine'));
$browser->info('Create a new Company');
$browser ->login() ->get('/company/new') ->with('form')->begin() ->fill('company', array('company[name]' => sfFactory::generate('Company'))) ->end()
->info('Create Company "'.sfFactory::last().'"') ->click('Save and add') ->with('form')->begin() ->hasErrors(false) ->end() ->followRedirect()
->with('doctrine')->begin() ->check('Company', array('name' => sfFactory::last())) ->end();
Verify record exists
Fill the form with a unique value
Initialize your testers
Wednesday, January 6, 2010
Success!
Wednesday, January 6, 2010
CAN YOU THINK OF OTHER USEFUL TESTERS?
Wednesday, January 6, 2010
USE PLUGINSswFunctionalTestGenerationPlugin
Add to your dev toolbar
Quickly stub symfony functional tests while clicking through the browser
Wednesday, January 6, 2010
sfTaskExtraPlugin$ ./symfony generate:plugin sfScraperPlugin
--module=sfScraper --test-application=frontend
SO YOU WANNA TEST YOUR PLUGINS?
Plugin Skeleton Test Fixtures
Wednesday, January 6, 2010
sfTaskExtraPlugin (continued)
$ ./symfony test:plugin sfScraperPlugin
$ ./symfony test:functional frontend sfScraperActions
$ ./symfony test:unit sfScraper
// config/ProjectConfiguration.class.phppublic function setupPlugins(){ $this->pluginConfigurations['sfScraperPlugin']->connectTests();}
Run all plugin tests
Run tests individually
Run plugin tests alongside project tests$ ./symfony test:all
Connect plugin tests in ProjectConfiguration and....
Wednesday, January 6, 2010
A LITTLE TASTE OF CUCUMBER
Uses “Scenarios”
that are then interpreted using
“Steps”
Scenario: See all vendors Given I am logged in as a user in the administrator role And There are 3 vendors When I go to the manage vendors page Then I should see the first 3 vendor names
$this->given("I am logged in as a user in the (.*) role", function($browser, $matches) { $browser->get('/') ->with('form')->begin() ->fill('login_'.$matches[1]) ->end() ->click('Sign in') ; });
Wednesday, January 6, 2010
CHECK OUT MORESelenium plugin to integrate with Symfony
- http://github.com/jatimo/cesTestSelenium
•Cucumber -
http://wiki.github.com/aslakhellesoy/cucumber
•Poocumber! -
http://github.com/bshaffer/Symfony-Snippets/tree/master/Poocumber/
•Presentation Notes -
http://github.com/bshaffer/Symfony-Snippets/tree/master/Presentation-Notes/
2010-02-05-FunctionalTesting
Wednesday, January 6, 2010
CONTACT
Brent [email protected]
http://brentertainment.comhttp://symplist.net
Wednesday, January 6, 2010
Top Related