Upload
drupalday
View
285
Download
0
Embed Size (px)
Citation preview
MANTENERE UNA DISTRIBUZIONE DRUPALATTRAVERSO TEST COVERAGE
PADDLE CASE STUDY
https://github.com/brummbar/drupal-day-2015
ABOUT ME
PER CHI LAVORO
COSA È PADDLE
FLEMISH GOVERNMENTFramework contract
Unico referente per sito web, hosting e supporto;frontend che segue le specifiche di branding del governoFiammingo;focus sulla gestione dei contenuti (revisioni, workflow);usabilità del backend per utenti non esperti;commenti, multilingua, contatti, quiz, rss, newsletter,protected pages.
WORKFLOW DI LAVORO
Sprint di 2 settimane↓
Soft release↓
Hard releaseRilascio in produzione
ORGANIZZAZIONE DELLO SPRINTOpen/ReopenedDevelopmentTestingQAUAT
ANATOMIA DI UN TICKETUser story / descrizione bugUAT stepsDefinition of done
DEFINITION OF DONEFunctionally completeAutomated tests cover UAT instructionsAutomated Simpletest tests passing within the last 24hAutomated Selenium tests passing within the last 24hWorking upgrade path from the last release in place...
AUTOMATED TESTSSimpletest e SeleniumShoov.io / WebdrivercssEseguiti su server tramite Jenkins CI
Esecuzioni giornaliere automaticheEsecuzioni manuali per ticket
Esecuzione remota su Browserstack
SELENIUMautomatizzazione di test su applicazioni webampiamente supportato in svariati linguaggiSelenium IDE e Webdriver
COSA TESTIAMO IN SELENIUMfunzionalità personalizzateintegrazioni tra modulifunzionalità Javascriptregressioni
QUALCHE NUMERO746 test10966 asserzionitempo di esecuzione di 18 orerichiedono 2/3 del tempo di sviluppo
PAGE OBJECT PATTERN
PUNTI CHIAVEriduzione della duplicazione del codicefacilità nell’aggiornare la relativa classe se gli elementidella pagina cambianopossibilità di sfruttare appieno i meccanismi di ereditarietà
REGOLEsolo la classe deve contenere XPath / selettori CSSrelativi all’elementonessuna assertion deve essere fatta all’interno dellaclasse
PAGE OBJECT PATTERN INPADDLE
STRUTTURA CARTELLE
ANATOMIA DI UNA CLASSE PAGE OBJECT
Il metodo costruttore contiene sempre una referenza aWebdriver e all'oggetto Selenium.
/** * Construct a new GlossaryDefinitionTableRow. * * @param WebDriverTestCase $webdriver * The Selenium web driver test case. * @param \PHPUnit_Extensions_Selenium2TestCase_Element $element * The webdriver element of the definition table row. */ public function __construct(WebDriverTestCase $webdriver, $element) { parent::__construct($webdriver); $this->element = $element; }
Tutte le proprietà che rappresentano altri Page Object sonogestite tramite magic get.
/** * {@inheritdoc} */ public function __get($name) { switch ($name) { case 'saveButton': return $this->element->byXPath('.//input[contains(@id, "edit-save")]' break; case 'definition': return new Text($this->webdriver, $this->element->byName('definition' break; case 'description': return new Wysiwyg($this->webdriver, 'edit-field-glossary-description-und-0-value' break; } throw new FormFieldNotDefinedException($name); }
PERCHÉ USARE __GET() ?Legacy codeSeparazione dalle proprietàLinearità di lettura$this->configurePage->go(); $this->configurePage->contextualToolbar->buttonAdd->click();
$modal->form->definition->fill($title); $modal->form->description->setBodyText($description); $modal->form->saveButton->click(); $modal->waitUntilClosed();
ESEMPIO DI TEST CASEGlossaryTest.php
testGlossaryDefinitionHighlighting
RISULTATO DEL TEST
SOPRAVVIVERE AI TESTApprocci e best practices
RANDOM FAILING TESTSPer RFT si indicano quei test che senza nessun
cambiamento nel codice o nei dati di test(*), il loro risultatonon è costante.Cause comuni:
differenze hardwaredati di test
IMPLICIT E EXPLICIT WAITSIl tempo di attesa implicito rappresenta per quanto tempoWebdriver deve interrogare il DOM in attesa di trovare un
elemento se esso non è disponibile immediatamente.Il tempo di attesa esplicito rappresenta per quanto tempoWebdriver deve aspettare una certa condizione prima di
proseguire con l'esecuzione.
IMPLICIT WAIT// Add a new definition. $this->configurePage->contextualToolbar->buttonAdd->click(); $modal = new GlossaryDefinitionModal($this); $modal->form->definition->fill('The awesome definition');
EXPLICIT WAIT// Add a new definition. $this->configurePage->contextualToolbar->buttonAdd->click(); $modal = new GlossaryDefinitionModal($this); $modal->waitUntilOpened(); $modal->form->definition->fill('The awesome definition');
public function waitUntilOpened() { $callable = new SerializableClosure(...); $this->webdriver->waitUntil($callable, $this->timeout); }
RANDOMIZZARE I DATI DI TESTPro
Evitare parzialmente "collisioni"tra contenutiTrovare casi limiteSimulare un vero utilizzo delsistema
Contro
La randomizzazione rende iltest non completamenteprevedibile
L'APPROCCIO DI PADDLELa generazione dei contenuti è randomizzata.I contenuti vengono lasciati intatti a meno che la loropresenza non complichi la realizzazione del test.
SOPRAVVIVERE AI TEST(REPRISE)
Approcci in Paddle
CKEDITOR/** * Sets the body text. * * @param string $text * The text to enter in the body. */ public function setBodyText($text) { $this->webdriver->byId('cke_' . $this->editorId); $this->webdriver->execute( array( 'script' => "CKEDITOR.instances['{$this->editorId}'].setData('" 'args' => array(), ) ); }
AJAX CALLBACKS// ... $form['list_id'] = array( '#type' => 'select', '#title' => t('List'), '#required' => TRUE, '#ajax' => array( 'callback' => 'mailchimp_campaign_list_segment_callback', ), ); // ...
/** * Marks the element as if it is waiting for AJAX callbacks. * * @see \Kanooh\Paddle\Pages\Element\Form\AutoCompletedText::markAsWaitingForAutoCompleteResults() */ public function markAsWaitingForAjaxCallback(\PHPUnit_Extensions_Selenium2TestCase_Element { $this->webdriver->execute( array( 'script' => "arguments[0].className += ' progress-disabled';", 'args' => array($element->toWebDriverObject()), ) ); }
MASTERING THE STALE// ... $form = $image_pane_type->getForm(); $form->showCaption->check(); // Submit the form and reload the modal. $form->submit();// Test something else. $form->captionTextArea->fill('Oooops'); // ...
EXPLOITING THE STALE// Waits until the current page is loaded. public function waitUntilPageIsLoaded() { $body = $this->bodyElement; $webdriver = $this->webdriver; $callable = new SerializableClosure( function () use ($body, $webdriver) { try { $body->click(); } catch (\PHPUnit_Extensions_Selenium2TestCase_WebDriverException return true; } } ); $this->webdriver->waitUntil($callable, $this->webdriver->getTimeout()); $this->webdriver->waitUntilElementIsDisplayed('//body'); }
IMPROVING SELENIUMRESULTS
RIPETIZIONI MULTIPLE PER EVITARE L'INTRODUZIONE DI RFT$ base_url=http://paddle.dev ./vendor/bin/phpunit --repeat=3 \ tests/Kanooh/Paddle/App/ContactPerson/EnableDisableTest.php
RICONOSCERE LE RFT$ ./vendor/bin/phpunit --repeat=2 --only-repeat-failed tests/
VELOCIZZARE L'ESECUZIONEesecuzione in parallelo dei testsfruttare drupal/drupal-driver per rimuovere interazionibrowser non necessarie
VISUAL REGRESSIONWEBDRIVERCSS E SHOOV.IO
WEBDRIVERCSSTool di automatizzazione per test di visual regression per
WebdriverIO.
SHOOV.IOTool di visual regression e live monitoring.
Wrapper per WebdrivercssHeadless Drupal per lo storage degli screenshotAngular.js web-app per il confronto visualeIntegrazione con Github (login, commits, pull request)Gratuito (?)
L'APPROCCIO DI PADDLE AL VRConfiguration
Single test
https://github.com/brummbar/visual-regression
Q&A
Per domande o suggerimenti, [email protected]
GRAZIE PERL'ATTENZIONE.