Serenity/JS - next generation acceptance testing for modern web applications

Preview:

Citation preview

@JanMolak @Wakaleo#Devoxx #SerenityJS

@JanMolak @Wakaleo#Devoxx #SerenityJS

@JanMolak @Wakaleo#Devoxx #SerenityJS

Jan Molak, John Smart:

Next GenerationAcceptance Testing

@JanMolak @Wakaleo#Devoxx #SerenityJS

acceptance tests that help deliver working software that matters

@JanMolak @Wakaleo#Devoxx #SerenityJS

what’s the root cause ofmost software defects?

@JanMolak @Wakaleo#Devoxx #SerenityJS

ambiguous, unclear and incorrectrequirements

@JanMolak @Wakaleo#Devoxx #SerenityJS

44-80%of all software defectssource:- 44% - “Out of Control - Why Control Systems Go Wrong and How to Prevent Failure”- 56% - “An Information Systems Manifesto”- 80% - “Requirements: A quick and inexpensive way to improve testing”

@JanMolak @Wakaleo#Devoxx #SerenityJS

Behaviour-Driven Development

@JanMolak @Wakaleo#Devoxx #SerenityJS

Domain-Driven DesignBehaviour-Driven Development

@JanMolak @Wakaleo#Devoxx #SerenityJS

User-Centred DesignDomain-Driven Design

Behaviour-Driven Development

@JanMolak @Wakaleo#Devoxx #SerenityJS

reliablescalable

actionable

acceptance tests that are:

@JanMolak @Wakaleo#Devoxx #SerenityJS

example

feature → scenario

@JanMolak @Wakaleo#Devoxx #SerenityJS

Feature:Filterthelisttofinditemsofinterest InordertolimitthecognitiveloadJameswouldliketofilterhistodolisttoonlyshowitemsofinterest

@JanMolak @Wakaleo#Devoxx #SerenityJS

Scenario:ViewingActiveitemsonlyGivenJameshasalistwithWalkthedog,GetacoffeeAndhecompletesWalkthedogWhenhefiltershislisttoshowonlyActivetasksThenhistodolistshouldcontainGetacoffee

@JanMolak @Wakaleo#Devoxx #SerenityJS

automation, take #1 “a test script”

@JanMolak @Wakaleo#Devoxx #SerenityJS

GivenJameshasalistwithWalkthedog,Getacoffee——————————————————————————————————————————————————————this.Given(/^.*hasalistwith(.*)$/,(items,done)=>{

done();});

@JanMolak @Wakaleo#Devoxx #SerenityJS

GivenJameshasalistwithWalkthedog,Getacoffee——————————————————————————————————————————————————————this.Given(/^.*hasalistwith(.*)$/,(items,done)=>{browser.get('http://todomvc.com/examples/angularjs/');

items.split(‘,’).forEach(item=>{element(by.id(‘new-todo’)).sendKeys(item);element(by.id(‘new-todo’)).sendKeys(protractor.Key.ENTER);});

done();});

@JanMolak @Wakaleo#Devoxx #SerenityJS

GivenJameshasalistwithWalkthedog,Getacoffee——————————————————————————————————————————————————————this.Given(/^.*hasalistwith(.*)$/,(items,done)=>{browser.get('http://todomvc.com/examples/angularjs/');

items.split(‘,’).forEach(item=>{element(by.id(‘new-todo’)).sendKeys(item);element(by.id(‘new-todo’)).sendKeys(protractor.Key.ENTER);});

done();});

@JanMolak @Wakaleo#Devoxx #SerenityJS

automation, take #2 “a re-structured test script”

@JanMolak @Wakaleo#Devoxx #SerenityJS

GivenJameshasalistwithWalkthedog,Getacoffee——————————————————————————————————————————————————————this.Given(/^.*hasalistwith(.*)$/,(items,done)=>{lettodoList=newTodoListPage();

todoList.get();

items.split(‘,’).forEach(item=>{todoList.add(item);});

done();});

@JanMolak @Wakaleo#Devoxx #SerenityJS

classTodoListPage{get(){browser.get('http://todomvc.com/examples/angularjs/');}add(item:string){element(by.id(‘new-todo’)).sendKeys(item); element(by.id('new-todo')).sendKeys(protractor.Key.ENTER); }complete(item:string){element(by.xpath('//*[@class="view"andcontains(.,"'+item+'")]'+ '//input[@type="checkbox"]')).click();}//…}

@JanMolak @Wakaleo#Devoxx #SerenityJS

classTodoListPage{get(){browser.get('http://todomvc.com/examples/angularjs/');}add(item:string){element(by.id(‘new-todo’)).sendKeys(item); element(by.id('new-todo')).sendKeys(protractor.Key.ENTER); }complete(item:string){element(by.xpath('//*[@class="view"andcontains(.,"'+item+'")]'+ '//input[@type="checkbox"]')).click();}//…}

@JanMolak @Wakaleo#Devoxx #SerenityJS

automation, take #3 “a screenplay”

@JanMolak @Wakaleo#Devoxx #SerenityJS

hierarchical task analysis

@JanMolak @Wakaleo#Devoxx #SerenityJS

hierarchical task analysis

actor

@JanMolak @Wakaleo#Devoxx #SerenityJS

hierarchical task analysis

goal

@JanMolak @Wakaleo#Devoxx #SerenityJS

hierarchical task analysis

tasks

@JanMolak @Wakaleo#Devoxx #SerenityJS

hierarchical task analysis

interactions

@JanMolak @Wakaleo#Devoxx #SerenityJS

Feature:FilterthelisttofinditemsofinterestInordertolimitthecognitiveloadJameswouldliketofilterhistodolisttoonlyshowitemsofinterest

Scenario:ViewingActiveitemsonlyGivenJameshasalistwithWalkthedog,GetacoffeeAndhecompletesWalkthedogWhenhefiltershislisttoshowonlyActivetasksThenhistodolistshouldcontainGetacoffee

@JanMolak @Wakaleo#Devoxx #SerenityJS

Feature:FilterthelisttofinditemsofinterestInordertolimitthecognitiveloadJameswouldliketofilterhistodolisttoonlyshowitemsofinterest

Scenario:ViewingActiveitemsonlyGivenJameshasalistwithWalkthedog,GetacoffeeAndhecompletesWalkthedogWhenhefiltershislisttoshowonlyActivetasksThenhistodolistshouldcontainGetacoffee

actor

@JanMolak @Wakaleo#Devoxx #SerenityJS

Feature:FilterthelisttofinditemsofinterestInordertolimitthecognitiveloadJameswouldliketofilterhistodolisttoonlyshowitemsofinterest

Scenario:ViewingActiveitemsonlyGivenJameshasalistwithWalkthedog,GetacoffeeAndhecompletesWalkthedogWhenhefiltershislisttoshowonlyActivetasksThenhistodolistshouldcontainGetacoffee

actor goal

@JanMolak @Wakaleo#Devoxx #SerenityJS

Scenario:ViewingActiveitemsonlyGivenJameshasalistwithWalkthedog,GetacoffeeAndhecompletesWalkthedogWhenhefiltershislisttoshowonlyActivetasksThenhistodolistshouldcontainGetacoffee

actor goal

tasks

@JanMolak @Wakaleo#Devoxx #SerenityJS

ToviewActiveitemsonly,Jamesattemptsto:

Startwithalistcontaining:Walkthedog,GetacoffeeCompleteatodoitemcalled:WalkthedogFilterlisttoshowonlyActivetasksExpecttosee:Getacoffee

actor goal

tasks

@JanMolak @Wakaleo#Devoxx #SerenityJS

ToviewActiveitemsonly,Jamesattemptsto:

Startwithalistcontaining:Walkthedog,GetacoffeeOpenbrowseron‘todomvc.com/examples/angularjs/'ResizebrowserwindowtomaximumAddatodoitemcalled‘Walkthedog’Addatodoitemcalled‘Getacoffee’...

actor goal

tasks

@JanMolak @Wakaleo#Devoxx #SerenityJS

ToviewActiveitemsonly,Jamesattemptsto:

Startwithalistcontaining:Walkthedog,GetacoffeeOpenbrowseron‘todomvc.com/examples/angularjs/'ResizebrowserwindowtomaximumAddatodoitemcalled‘Walkthedog’Addatodoitemcalled‘Getacoffee’Enterthevalue‘Getacoffee’HittheEnterkey...

actor goal tasks inter-

actions

@JanMolak @Wakaleo#Devoxx #SerenityJS

ToviewActiveitemsonly,Jamesattemptsto:

Startwithalistcontaining:Walkthedog,GetacoffeeOpenbrowseron‘todomvc.com/examples/angularjs/'ResizebrowserwindowtomaximumAddatodoitemcalled‘Walkthedog’Addatodoitemcalled‘Getacoffee’Enterthevalue‘Getacoffee’HittheEnterkey...

@JanMolak @Wakaleo#Devoxx #SerenityJS

ToviewActiveitemsonly,Jamesattemptsto:

Start.withATodoListContaining(‘Walkthedog’,…)Open.browserOn(‘todomvc.com/examples/angularjs/’)ResizeBrowserWindow.toMaximum()AddATodoItem.called(‘Walkthedog’)AddATodoItem.called(‘Getacoffee’)Enter.theValue(‘Getacoffee’).into(TodoList.New_Todo_Field).thenHit(protractor.Key.Enter)

@JanMolak @Wakaleo#Devoxx #SerenityJS

ToviewActiveitemsonly,Jamesattemptsto:

Start.withATodoListContaining(‘Walkthedog’,…)Open.browserOn(‘todomvc.com/examples/angularjs/’)ResizeBrowserWindow.toMaximum()AddATodoItem.called(‘Walkthedog’)AddATodoItem.called(‘Getacoffee’)Enter.theValue(‘Getacoffee’).into(TodoList.New_Todo_Field).thenHit(protractor.Key.Enter)

@JanMolak @Wakaleo#Devoxx #SerenityJS

Serenity/JS Screenplay Pattern

@JanMolak @Wakaleo#Devoxx #SerenityJS

letjames=Actor.named(‘James’);actor

@JanMolak @Wakaleo#Devoxx #SerenityJS

letjames=Actor.named(‘James’).whoCan(BrowseTheWeb.using(protractor.browser));

actor has

abilities

@JanMolak @Wakaleo#Devoxx #SerenityJS

james.attemptsTo(Start.withATodoListContaining(items));

actor performs

tasks

@JanMolak @Wakaleo#Devoxx #SerenityJS

this.Given(/^.*hasalistwith(.*)$/,(items)=>{returnjames.attemptsTo(Start.withATodoListContaining(items));});

actor performs

tasks

@JanMolak @Wakaleo#Devoxx #SerenityJS

exportclassStartimplementsTask{performAs(actor:PerformsTasks):PromiseLike<void>{returnactor.attemptsTo(Open.browserOn('/examples/angularjs/'),ResizeBrowserWindow.toMaximum(),AddTodoItems.called(this.initialItems));}}

tasks consist of

tasks

@JanMolak @Wakaleo#Devoxx #SerenityJS

exportclassAddATodoItemimplementsTask{performAs(actor:PerformsTasks):PromiseLike<void>{returnactor.attemptsTo(Enter.theValue(‘Walkthedog’).into(TodoList.New_Todo_Field),Hit.the(protractor.Key.Enter).into(ToDoList.New_Todo_Field) ); }}

tasks consist of

inter-actions

@JanMolak @Wakaleo#Devoxx #SerenityJS

exportclassAddATodoItemimplementsTask{

@step('{0}addsatodoitemcalled"#name"')performAs(actor:PerformsTasks):PromiseLike<void>{//…}

constructor(privatename:string){}}

tasks can be

annotated

@JanMolak @Wakaleo#Devoxx #SerenityJS

to create powerful reports

@JanMolak @Wakaleo#Devoxx #SerenityJS

lights, camera,Demo!

@JanMolak @Wakaleo#Devoxx #SerenityJS

github.com/jan-molak/serenity-js