Acceptance & Functional Testing with Codeception - Devspace 2015
Test-driven Development with Drupal and Codeception (DrupalCamp Brighton)
Transcript of Test-driven Development with Drupal and Codeception (DrupalCamp Brighton)
WHAT IS TDD?
- Scott Wambler
[Test driven development] is an evolutionaryapproach to development which combines test-first development where you write a test beforeyou write just enough production code to fulfill
that test and refactoring.Introduction to Test Driven Development (TDD)
TDD WORKFLOWWrite a testRun your testsWatch them failWrite enough code to make your tests passRun your tests againRefactor/tidy upRun your tests againRepeat...
LET YOUR TESTS DRIVE YOUR DEVELOPMENT!
WHY USE TDD?For the entire lifespan of your project, you have tests for thefeatures you build.Forces you to think about features before implementation (doyou really need to install that really cool module?)Speeds up developmentEnsures you write good quality code/build good qualityfeaturesIt's fun! No, really!Again; for the entire lifespan of your project, you have tests forthe features you build
WHY CHOOSE CODECEPTION?Powered by PHPUnitVERY easy to extendRun any type of test you may want to
AcceptanceFunctionalUnitOr build your own suite with a combination of providedmodules (CLI suite for custom Drush commands? You gotit!)
Run all your different test types from one source
WHAT FEATURE DO WE WANT TOIMPLEMENT?
Scenario: Authors should be able to create Articles Given I have a user named "Bill" And "Bill" has the role of Author When "Bill" tries to create an Article node Then "Bill" should see the node/add page for an Article And "Bill" should be able to create an Article node
./TESTS/ACCEPTANCE/AUTHORPERMISSIONCEST.PHP
<?php use \AcceptanceTester;
class AuthorPermissionsCest {
public function _before(AcceptanceTester $I) { }
public function _after(AcceptanceTester $I) { }
// tests public function tryToTest(AcceptanceTester $I) { } }
public function _before(AcceptanceTester $I)
{
$this->faker = Faker::create();
}
public function an_author_can_create_article_content(AcceptanceTester $I)
{
$I->am('an author');
$I->wantTo('create an Article');
$I->expect('to be able to create an Article node.');
$I->amOnPage('/user/login');
$I->submitForm('#user-login', ['name' => 'Bill', 'pass' => 'password']);
$I->seeElement('body.logged-in');
$I->amOnPage('/node/add/article');
$I->seeResponseCodeIs('200');
$node_title = $this->faker->text(30);
$I->submitForm(
'#article-node-form',
[
'title' => $node_title,
'body[und][0][value]' => $this->faker->text(400),
]
);
$I->see($node_title, '#page-title');
}
WHAT IS A PAGEOBJECT
- Codeception docs
The PageObject pattern represents a web page
as a class and the DOM elements on that page as
its properties, and some basic interactions as its
methods.
Advanced Usage
EXAMPLE PAGEOBJECT
<?php class LoginPage {
public static $URL = '/user/login';
public static $usernameField = '#edit-name';
public static $passwordField = '#edit-pass';
protected $acceptanceTester;
public static function route() { return static::$URL; }
}
PAGEOBJECT USAGE
public function an_author_can_create_article_content(AcceptanceTester $I)
{
...
$I->amOnPage(LoginPage::$URL);
$I->submitForm(LoginPage::$formId,
[
LoginPage::$usernameField => 'Bill',
LoginPage::$passwordField => 'password'
]
);
$I->seeElement('body.logged-in');
...
}
WHAT IS A STEPOBJECT?A StepObject is a class consisting of methods containing actions
(steps) that are run.
BEFORE
$I->amOnPage('/user/login');
$I->submitForm('#user-login',
[
'name' => 'Bill',
'pass' => 'password'
]
);
AFTER
$I->login('Bill', 'password');
EXAMPLE STEPOBJECT
...
use LoginPage;
class UserSteps extends \AcceptanceTester {
public function login($userName, $userPassword)
{
$I = $this;
$I->amOnPage(LoginPage::$URL);
$I->submitForm(LoginPage::$formId,
[
LoginPage::$usernameField => $userName,
LoginPage::$passwordField => $userPassword
]
);
}
...
}
STEPOBJECT USAGE
use AcceptanceTester\UserSteps; ...
public function an_author_can_create_article_content(UserSteps $I) { ...
$I->login('Bill', 'password'); $I->seeElement('body.logged-in');
... }
REFACTORING TIMECopy and paste once - any more, extract to a StepObject or
PageObject
A readable test is a useful test; if a set of steps would benefit
from a description, then extract to a StepObject
HOW DID OUR TEST LOOK?public function an_author_can_create_article_content(AcceptanceTester $I)
{
$I->am('an author');
$I->wantTo('create an Article');
$I->expect('to be able to create an Article node.');
$I->amOnPage('/user/login');
$I->submitForm('#user-login', ['name' => 'Bill', 'pass' => 'password']);
$I->seeElement('body.logged-in');
$I->amOnPage('/node/add/article');
$I->seeResponseCodeIs('200');
$node_title = $this->faker->text(30);
$I->submitForm(
'#article-node-form',
[
'title' => $node_title,
'body[und][0][value]' => $this->faker->text(400),
]
);
$I->see($node_title, '#page-title');
}
HOW DOES OUR TEST LOOK NOW?use ArticleNode;
public function an_author_can_create_article_content(AcceptanceTester $I)
{
$I->am('an author');
$I->wantTo('create an Article');
$I->expect('to be able to create an Article node.');
$I->login('Bill', 'password');
$I->amOnPage(ArticleNode::$nodeAddFormURL);
$I->seeResponseCodeIs('200');
$node_title = $this->faker->text(30);
$I->submitForm(
ArticleNode::$nodeFormId,
[
ArticleNode::$titleEditField => $node_title,
ArticleNode::$bodyEditField => $this->faker->text(400),
]
);
$I->see($node_title, ArticleNode::$pageTitleSelector);
}
USEFUL EXTENSIONS
A set of standard page objects for use in Codeception tests onDrupal sites.
codeception-drupal-pages
A Codeception module for managing test usersDrupal User Registry
A Codeception module to provide a set of classes thatencapsulate Drupal content types.
Drupal Content Type Registry
The Codeception extension for automatically starting andstopping PhantomJS when running tests.
Phantoman
Codeception curated list of addonsCodeception Addons page