Test-driven Development with Drupal and Codeception (DrupalCamp Brighton)

40
TEST DRIVEN DEVELOPMENT WITH DRUPAL AND CODECEPTION

Transcript of Test-driven Development with Drupal and Codeception (DrupalCamp Brighton)

TEST DRIVENDEVELOPMENT WITH

DRUPAL ANDCODECEPTION

WHO AM I?MATT CHAPMANDEVELOPER

TWITTER: @CHAPABUDRUPAL.ORG: /U/CHAPABU

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

WHAT IS CODECEPTIONPHP Test framework

Can run almost any kind of test you can think of

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

INSTALLATION

composer require codeception/codeception --dev

SETUP

vendor/bin/codecept bootstrap

PROTIP

Add vendor/bin to your $PATH

EXAMPLE TIME!

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

LET'S START WITH A TEST

codecept generate:cest acceptance AuthorPermissions

./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) { } }

HOW MIGHT OUR TEST LOOK?

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');

}

LET'S RUN IT

WHY DID IT FAIL?We haven't added the permission to the user role.

Let's add it and re-run the test.

EXTENDING CODECEPTION

PAGEOBJECTS

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

CREATING PAGEOBJECTS

codecept generate:pageobject acceptance LoginPage

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');

...

}

STEPOBJECTS

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');

CREATING STEPOBJECTS

codecept generate:stepobject acceptance UserSteps

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');

}

WE CREATE PAGEOBJECTS ANDSTEPOBJECTS...

PageObjects

LoginPage

ArticleNode

StepObjects

UserSteps

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

POSSIBLE ALTERNATIVESPHPUnit

PHPSpec

CasperJS

Selenium

WE'RE HIRINGSENIOR FRONT END DEVELOPER

GRADUATE DEVELOPER

THANK YOU!