60
REFACTORING USING CODECEPTION

Refactoring using Codeception

Embed Size (px)

DESCRIPTION

You must’ve heard of Unit testing… If not, then this talk is definitely for you! If you do know Unit testing, you probably ran at some point into a hurdle: “Where do I start?” And despite your best efforts, you end up not having enough tests for your application – Then that change request comes in, requiring you to change that very same complex piece of code for which you are lacking tests! How do you going refactor while maintaining all those ‘undocumented’ business rules? This talk will show how Codeception can be leveraged to refactor the visuals aspects of an application, maintaining backwards compatibility on API changes and even assist in moving to a whole different server infrastructure.

Citation preview

Page 1: Refactoring using Codeception

REFACTORING USING CODECEPTION

Page 2: Refactoring using Codeception

$I->CANSEE(‘JEROEN’, ‘#VANDIJK’)

Page 3: Refactoring using Codeception

$I->USETWITTER(‘@JRVANDIJK’)

Page 4: Refactoring using Codeception

$I->AM(‘PHPBNL’, ‘.BOARD-MEMBER’)

Page 5: Refactoring using Codeception

$I->WORK(‘#ENRISE’)

Page 6: Refactoring using Codeception
Page 7: Refactoring using Codeception

NETHERLANDS

UNITED KINGDOM

BELGIUM

GERMANY

AMSTERDAM

Page 8: Refactoring using Codeception

NETHERLANDS

UNITED KINGDOM

BELGIUM

GERMANY

AMSTERDAM

NOT AMSTERDAM

Page 9: Refactoring using Codeception

NETHERLANDS

UNITED KINGDOM

BELGIUM

GERMANY

AMERSFOORT

Page 10: Refactoring using Codeception

GRAIN WAREHOUSE

Page 11: Refactoring using Codeception

REFACTORED

Page 12: Refactoring using Codeception

THIS TALK IS NOT…

Page 13: Refactoring using Codeception

THIS TALK IS NOT…

DEPENDENCY INJECTION, DECOUPLING, ENCAPSULATION, TESTABLE CODE

REFACTORING

Page 14: Refactoring using Codeception

A SIMPLE BDD STYLE TESTING FRAMEWORK WHICH IS EASY TO READ, WRITE AND DEBUG

WHAT IS CODECEPTION?

Page 15: Refactoring using Codeception

YOU WRITE IN YOUR FAVORITE EDITOR

Page 16: Refactoring using Codeception

VENDOR/BIN/CODECEPT BOOTSTRAP

COMPOSER INSTALL CODECEPTION/CODECEPTION

Page 17: Refactoring using Codeception

class_name: AcceptanceTester modules: enabled: - PhpBrowser - AcceptanceHelper config: PhpBrowser: url: 'http://www.zendcon.com/'

ACCEPTANCE.SUITE.YML

Page 18: Refactoring using Codeception

PAGE USED FOR TESTING

Page 19: Refactoring using Codeception

public function seeIfNameExists(\AcceptanceTester $I) { $I->wantTo('see if conference name exists'); $I->amOnPage(‘/'); $I->click(‘#rt-logo‘); $I->see('zendcon'); }

CODECEPT GENERATE:CEST ACCEPTANCE HOME

Page 20: Refactoring using Codeception

STEPS

Page 21: Refactoring using Codeception

GENERATED FILE EXTENDS ACCEPTANCETESTER CLASS

CODECEPT GENERATE:STEPOBJECT ACCEPTANCE <NAME>

Page 22: Refactoring using Codeception

class CompareSteps extends \AcceptanceTester { public function seeIfNameExists() { $I = $this; $I->amOnPage('/'); $I->see('zendcon'); } }

class MenuCest { public function seeIfNameExistsViaCCStep(\CompareSteps $I) { $I->seeIfNameExists(); } }

REUSE CODE FOR DIFFERENT TESTS

Page 23: Refactoring using Codeception

PAGE OBJECTS

Page 24: Refactoring using Codeception

GENERATED FILE IS JUST A CONTAINER

CODECEPT GENERATE:PAGEOBJECT ACCEPTANCE <NAME>

Page 25: Refactoring using Codeception

class HomePage { public static $URL = '/'; … // removed code for slide layout purposes public static function of(AcceptanceTester $I) { return new static($I); }

public function see($value) { $I = $this->acceptanceTester; $I->amOnPage(self::$URL); $I->see($value); } }

PAGE OBJECT CONTAINER

Page 26: Refactoring using Codeception

public function seeIfNameExistsViaPageObject() { HomePage::of($this)->see('zendcon'); }

USE THE OBJECT IN A TEST

Page 27: Refactoring using Codeception

VERSION COMPARISON

Page 28: Refactoring using Codeception

FROM A TECHNICAL PERSPECTIVE

MASTER !== RELEASE/NEXTGEN

Page 29: Refactoring using Codeception

FROM A FUNCTIONAL PERSPECTIVE

MASTER === RELEASE/NEXTGEN

Page 30: Refactoring using Codeception

LOST OF CODE COMING UP…

ATTENTION PLEASE

Page 31: Refactoring using Codeception

public function getHtmlFromContent(InnerBrowser $innerBrowser, $css) { $crawler = $this->getCrawler($innerBrowser); $selector = CssSelector::toXPath($css); $value = $crawler->filterXPath($selector); return $value->html(); }

protected function getCrawler(InnerBrowser $innerBrowser) { $reflection = new \ReflectionClass(get_class($innerBrowser)); $property = $reflection->getProperty('crawler'); $property->setAccessible(true); return $property->getValue($innerBrowser); }

OVERRIDE DEFAULT CRAWLER

Page 32: Refactoring using Codeception

protected function getPhpBrowserByPage($page) { $phpBrowser = $this->getAlternatePhpBrowser(); $phpBrowser->amOnPage($page); return $phpBrowser; }

protected function getAlternatePhpBrowser() { $config = Configuration::config(); $suite = Configuration::suiteSettings('acceptance', $config); $options = $suite['modules']['config']['PhpBrowser']; $options['url'] = $options['alternate-url']; $phpBrowser = new PhpBrowser($options)->_initialize(); $this->setProxyInGuzzle($phpBrowser->guzzle); return $phpBrowser; }

CREATE SECOND PHPBROWSER INSTANCE

Page 33: Refactoring using Codeception

public function getHtml($page, $path) { $I = $this; $I->amOnPage($page); return $this->getHtmlFromContent( $I->fetchModule('PhpBrowser'), $path); }

public function getAlternateHtml($page, $path) { return $this->getHtmlFromContent( $this->getPhpBrowserByPage($page), $path); }

GET HTML OF BOTH VERSIONS

Page 34: Refactoring using Codeception

class_name: AcceptanceTester modules: enabled: - PhpBrowser - AcceptanceHelper config: PhpBrowser: url: 'http://www.zendcon.com/' alternate-url: 'http://zendcon.com'

ADDING ALTERNATE URL

Page 35: Refactoring using Codeception

public function seeSameOnVersions($page, $path, $altPath, $message) { $I = $this; list($left, $right) = $this->getContentFromVersions(

$page, $path, $altPath); $I->seeEquals($left, $right, $message); }

public function getContentFromVersions($page, $path, $altPath) { return array( $this->getHtml($page, $path), $this->getAlternateHtml($page, $altPath) ); }

COMPARING 2 VERSIONS IN 1 RUN

Page 36: Refactoring using Codeception

public function seeIfPageHeaderIsIdentical(\CompareSteps $I) { $I->seeSameOnVersions( \HomePage::$URL, 'h2', 'h2', 'Homepage header not identical' ); }

TEST PAGE HEADER

Page 37: Refactoring using Codeception

public function seeIfFormActionIsIdentical(\CompareSteps $I) { $I->seeSameOnVersions( \HomePage::$URL, '.rsformbox1', '.rsformbox1', 'Homepage signup form not identical' ); }

TEST SIGNUP FORM

Page 38: Refactoring using Codeception

public function seeIfFormActionIsIdentical(\CompareSteps $I) { $I->seeSameOnVersions( \HomePage::$URL, '.rsformbox1', '.rsformbox1', 'Homepage signup form not identical' ); }

<div class="rsformbox1 title3"> - <form method=“post" id="userForm" action="http://www.zendcon.com/"> + <form method="post" id="userForm" action="http://zendcon.com/">

TEST SIGNUP FORM

Page 39: Refactoring using Codeception

RUNNING THE TESTS!

Page 40: Refactoring using Codeception

EXAMPLES?

Page 41: Refactoring using Codeception

USER SPECIFIC SETTINGS

Page 42: Refactoring using Codeception

CODECEPTION.YML

VS

CODECEPTION.DIST.YML

CODECEPTION.YML.DIST

Page 43: Refactoring using Codeception

TESTING AN API

Page 44: Refactoring using Codeception

class_name: ApiTester modules: enabled: - ApiHelper - PhpBrowser - REST config: PhpBrowser: url: https://api.github.com REST: url: https://api.github.com

Page 45: Refactoring using Codeception

public function testGetGists(ApiTester $I) { $I->wantTo('see if we can get the gists listing'); $I->haveHttpHeader('Accept', 'application/vnd.github.beta+json'); $I->sendGet('/users/weierophinney/gists'); $I->seeResponseCodeIs(200); $I->seeResponseIsJson(); }

public function testGetGist(ApiTester $I) { $I->wantTo('see if we can get a gist'); $I->haveHttpHeader('Accept', 'application/vnd.github.beta+json'); $I->sendGet('/gists/2c47c9d59f4a5214f0c3'); $I->seeResponseCodeIs(200); $I->seeResponseIsJson(); }

Page 46: Refactoring using Codeception

ENVIRONMENTS

Page 47: Refactoring using Codeception

/** * @env beta */ public function testGetOldVersionGist(ApiTester $I) { $I->wantTo('see if we can get a gist'); $I->haveHttpHeader('Accept', $I->getAcceptHeader()); $I->sendGet('/gists/2c47c9d59f4a5214f0c3'); $I->seeResponseCodeIs(200); $I->seeResponseIsJson(); $I->seeResponseContainsJson( array('user' => array('login' => ‘weierophinney') )); }

Page 48: Refactoring using Codeception

/** * @env version3 */ public function testGetOldVersionGist(ApiTester $I) { $I->wantTo('see if we can get a gist'); $I->haveHttpHeader('Accept', $I->getAcceptHeader()); $I->sendGet('/gists/2c47c9d59f4a5214f0c3'); $I->seeResponseCodeIs(200); $I->seeResponseIsJson(); $I->seeResponseContainsJson( array('owner' => array('login' => ‘weierophinney') )); }

SPOT THE DIFFERENCE

Page 49: Refactoring using Codeception

env: beta: config: data: accept: application/vnd.github.beta+json version3: config: data: accept: application/vnd.github.v3+json

SUITE CONFIG ADDITIONS

Page 50: Refactoring using Codeception

TESTING 2 API VERSION IN 1 RUN

CODECEPT RUN API —ENV BETA —ENV VERSION3

Page 51: Refactoring using Codeception

EXAMPLES?

Page 52: Refactoring using Codeception

READY TO DIG DEEPER?

USING MODULES

Page 53: Refactoring using Codeception

USE YOUR IMAGINATION

CODECEPT GENERATE:SUITE

Page 54: Refactoring using Codeception

class MigrateHelper extends \Codeception\Module { public function seeIfLineExistsInFile($file, $line) { $filesystem = $this->getModule('Filesystem'); $filesystem->seeFileFound($file); $filesystem->seeInThisFile($line); } }

class HostCest { public function testIfHostsFileIsConfigured(MigrateTester $I) { $I->seeIfLineExistsInFile('/etc/hosts', '127.0.0.1'); } }

FILESYSTEM MODULE

Page 55: Refactoring using Codeception

class MigrateHelper extends \Codeception\Module { public function seeIfPortIsReachable($host, $port) { $cli = $this->getModule('Cli'); $cli->runShellCommand('nmap '.$host.' -Pn -p '.$port); $cli->seeInShellOutput($port.'/tcp open'); } }

class HostCest { public function testIfPortReachable(MigrateTester $I) { $I->seeIfPortIsReachable('www.zendcon.com', 80); } }

CLI MODULE

Page 56: Refactoring using Codeception

class MigrateHelper extends \Codeception\Module { public function seeAddressIsMatchingIp($address, $ip) { $cli = $this->getModule('Cli'); $cli->runShellCommand('host '.$address); $cli->seeInShellOutput($address . ' has address '.$ip); } }

class HostCest { public function testIfDnsCanBeResolved(MigrateTester $I) { $I->seeAddressIsMatchingIp('zendcon.com', '50.56.0.87'); } }

CLI MODULE

Page 57: Refactoring using Codeception

class MigrateHelper extends \Codeception\Module { public function seeContentsInRemoteFile($file, $line) { $server = $this->getModule('FTP'); $server->seeFileFound(basename($file), dirname($file)); $server->openFile($file); $server->seeInThisFile($line); } }

class HostCest { public function testIfRemoteFileHasContents(MigrateTester $I) { $I->seeContentsInRemoteFile('/etc/hosts', '127.0.0.1'); } }

FTP MODULE

Page 58: Refactoring using Codeception

FTP MODULE SIGNS IN BEFORE EVERY TEST

CAVEAT!

Page 59: Refactoring using Codeception

RUNNING THE TESTS!

Page 60: Refactoring using Codeception

GITHUB.COM/JVANDIJK/ZC14-CODECEPTION

JOIND.IN/11997