Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Creating Faster and More Reliable Web Tests with Blended Testing

Creating Faster and More Reliable Web Tests with Blended Testing

Automated acceptance tests are often implemented to exercise a fully-assembled and deployed system through its web interface. This approach makes them slow and flaky and reduces the value they could bring to your organisation.

Blended Testing is a technique that can improve the speed and stability of web-based test scenarios by making them take shortcuts through other interfaces of the system under test.
To use it effectively, though, we need to know when it's safe to take shortcuts and how to take them - this is where Task Analysis and the Screenplay Pattern can help.

In this talk, Jan Molak - Lead Developer of Serenity/JS and co-author of "BDD in Action, Second Edtion", will show you how to apply Task Analysis, Screenplay Pattern and use Serenity/JS 3.0 to design and develop blended test scenarios that exercise multiple interfaces of the system under test to improve execution speed and stability.

---

Video recording: https://www.youtube.com/@serenity-js
Serenity/JS website: https://serenity-js.org/

Jan Molak

April 04, 2023
Tweet

Transcript

  1. Jan Molak Lead Developer of Serenity/JS Contributor to the Screenplay

    Pattern Co-author of "BDD in Action, 2nd edition" Trainer, Consultant janmolak.com Blended Testing
  2. Jan Molak | serenity-js.org Blended Testing Interacting with multiple interfaces

    of the system under test
 to improve their stability, reliability, and performance 
 without reducing their coverage.
  3. Jan Molak | serenity-js.org Tool-centric E2E automation • Scenarios focus

    on interacting with one interface at the time System
 Under Test Web UI System
 State
  4. Jan Molak | serenity-js.org Tool-centric E2E automation • Scenarios focus

    on interacting with one interface at the time System
 Under Test Web UI System
 State Web Integration Tool
  5. Jan Molak | serenity-js.org Tool-centric E2E automation • Scenarios focus

    on interacting with one interface at the time System
 Under Test Web Test Suite Web UI System
 State Web Integration Tool
  6. Jan Molak | serenity-js.org Tool-centric E2E automation • Scenarios focus

    on interacting with one interface at the time • Test suites organised per-interface System
 Under Test Web Test Suite Web UI REST API System
 State Web Integration Tool
  7. Jan Molak | serenity-js.org Tool-centric E2E automation • Scenarios focus

    on interacting with one interface at the time • Test suites organised per-interface System
 Under Test Web Test Suite Web UI REST API System
 State Web Integration Tool API Integration Tool
  8. Jan Molak | serenity-js.org Tool-centric E2E automation • Scenarios focus

    on interacting with one interface at the time • Test suites organised per-interface System
 Under Test Web Test Suite Web UI REST API API Test Suite System
 State Web Integration Tool API Integration Tool
  9. Jan Molak | serenity-js.org Tool-centric E2E automation • Scenarios focus

    on interacting with one interface at the time • Test suites organised per-interface • Little or no code reuse => high maintenance or poor coverage System
 Under Test Web Test Suite Web UI REST API API Test Suite System
 State Web Integration Tool API Integration Tool
  10. Jan Molak | serenity-js.org Tool-centric E2E automation • Scenarios focus

    on interacting with one interface at the time • Test suites organised per-interface • Little or no code reuse => high maintenance or poor coverage System
 Under Test Web Test Suite Web UI Mobile App REST API API Test Suite System
 State Web Integration Tool API Integration Tool ?
  11. Jan Molak | serenity-js.org Tool-centric E2E automation • Scenarios focus

    on interacting with one interface at the time • Test suites organised per-interface • Little or no code reuse => high maintenance or poor coverage System
 Under Test Web Test Suite Web UI Mobile App REST API API Test Suite System
 State Web Integration Tool API Integration Tool Messaging API ? ?
  12. Jan Molak | serenity-js.org Tool-centric E2E automation • Scenarios focus

    on interacting with one interface at the time • Test suites organised per-interface • Little or no code reuse => high maintenance or poor coverage System
 Under Test Web Test Suite Web UI Mobile App REST API API Test Suite System
 State Web Integration Tool API Integration Tool Batch API Messaging API ? ? ?
  13. Jan Molak | serenity-js.org Tool-centric E2E automation Pick "the best

    tool" for the job - too many jobs, too many tools
  14. Jan Molak | serenity-js.org UI-centric E2E automation Pick "the best

    tool" for the job - too many jobs, too many tools Check the UI, like the user would
  15. Jan Molak | serenity-js.org UI-centric E2E automation • Scenarios only

    ever interact with the web UI System
 Under Test Web UI Mobile App REST API System
 State Batch API Messaging API
  16. Jan Molak | serenity-js.org UI-centric E2E automation • Scenarios only

    ever interact with the web UI System
 Under Test Web UI Mobile App REST API System
 State Web Integration Tool Batch API Messaging API
  17. Jan Molak | serenity-js.org UI-centric E2E automation • Scenarios only

    ever interact with the web UI System
 Under Test Web Test Suite Web UI Mobile App REST API System
 State Web Integration Tool Batch API Messaging API
  18. Jan Molak | serenity-js.org UI-centric E2E automation • Scenarios only

    ever interact with the web UI • Interactions with other interfaces accidental, ad-hoc, or missed System
 Under Test Web Test Suite Web UI Mobile App REST API System
 State Web Integration Tool Batch API Messaging API
  19. Jan Molak | serenity-js.org UI-centric E2E automation • Scenarios only

    ever interact with the web UI • Interactions with other interfaces accidental, ad-hoc, or missed System
 Under Test Web Test Suite Web UI Mobile App REST API System
 State Web Integration Tool Batch API Messaging API
  20. Jan Molak | serenity-js.org UI-centric E2E automation • Scenarios only

    ever interact with the web UI • Interactions with other interfaces accidental, ad-hoc, or missed System
 Under Test Web Test Suite Web UI Mobile App REST API System
 State Web Integration Tool Batch API Messaging API ? ?
  21. Jan Molak | serenity-js.org UI-centric E2E automation • Scenarios only

    ever interact with the web UI • Interactions with other interfaces accidental, ad-hoc, or missed • Slow and unreliable tests => high failure diagnosis costs System
 Under Test Web Test Suite Web UI Mobile App REST API System
 State Web Integration Tool Batch API Messaging API ? ?
  22. Jan Molak | serenity-js.org UI-centric E2E automation • Scenarios only

    ever interact with the web UI • Interactions with other interfaces accidental, ad-hoc, or missed • Slow and unreliable tests => high failure diagnosis costs System
 Under Test Web Test Suite Web UI Mobile App REST API System
 State Web Integration Tool Batch API Messaging API ? ? API Test Suite API Integration Tool
  23. Jan Molak | serenity-js.org UI-centric E2E automation Pick "the best

    tool" for the job - too many jobs, too many tools Check the UI, like the user would - users don't care about the UI
  24. Jan Molak | serenity-js.org Blended testing Pick "the best tool"

    for the job - too many jobs, too many tools Check the UI, like the user would - users don't care about the UI Focus on actors, blend interfaces and tools
  25. Jan Molak | serenity-js.org UCD mental model for Blended Testing

    Roles - Who? ↳ Goals - Why? ↳ Tasks - What?
  26. Jan Molak | serenity-js.org UCD mental model for Blended Testing

    Roles - Who? ↳ Goals - Why? ↳ Tasks - What? ↳ Interactions - How? Mental model inspired by:
 - "In praise of abstraction" by Kevin Lawrence, 2007 - "A bit of UCD for BDD and ATDD" by Antony Marcano, 2011
  27. Jan Molak | serenity-js.org UCD mental model for Blended Testing

    Roles - Who? ↳ Goals - Why? ↳ Tasks - What? ↳ Interactions - How? ↳ Integration tools ↳ Interfaces - Implementation detail
  28. Jan Molak | serenity-js.org Blended testing • Acceptance tests focus

    on actors, goals, and tasks • Use only interfaces necessary to do a meaningful test System
 Under Test Acceptance Test Suite System
 State Integration Tools Interfaces Test Automation Framework
  29. Jan Molak | serenity-js.org Blended testing • Acceptance tests focus

    on actors, goals, and tasks • Use only interfaces necessary to do a meaningful test • Building multimodal automation frameworks is really hard System
 Under Test Acceptance Test Suite System
 State Integration Tools Interfaces Test Automation Framework ?
  30. Jan Molak | serenity-js.org Screenplay Pattern Using your domain language

    to implement test scenarios
 that model what tasks the actors need to perform 
 in order to accomplish their goals 
 when interacting with the system under test.
  31. Jan Molak | serenity-js.org Screenplay Pattern: Actors and roles Feature:

    Deals on apples Scenario: 3-for-2 on apples Given a pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 3 packs of apples Then they should be requested to pay $5.00 Scenario: 3-for-2 deal reminder Given a pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 2 packs of apples Then they should be requested to pay $5.00 But they should be reminded about the 3-for-2 deal
  32. Jan Molak | serenity-js.org Screenplay Pattern: Actors and roles Feature:

    Deals on apples Scenario: 3-for-2 on apples Given a pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 3 packs of apples Then they should be requested to pay $5.00 Scenario: 3-for-2 deal reminder Given a pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 2 packs of apples Then they should be requested to pay $5.00 But they should be reminded about the 3-for-2 deal
  33. Feature: Deals on apples Scenario: 3-for-2 on apples Given a

    pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 3 packs of apples Then they should be requested to pay $5.00 Scenario: 3-for-2 deal reminder Given a pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 2 packs of apples Then they should be requested to pay $5.00 But they should be reminded about the 3-for-2 deal import {actorCalled} from '@serenity-js/core' actorCalled('Shopper') Jan Molak | serenity-js.org Screenplay Pattern: Actors and roles
  34. Feature: Deals on apples Scenario: 3-for-2 on apples Given a

    pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 3 packs of apples Then they should be requested to pay $5.00 Scenario: 3-for-2 deal reminder Given a pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 2 packs of apples Then they should be requested to pay $5.00 But they should be reminded about the 3-for-2 deal import {actorCalled} from '@serenity-js/core' // Shopper role: // - finds products of interest // - manages basket // - makes payments actorCalled('Shopper') Jan Molak | serenity-js.org Screenplay Pattern: Actors and roles
  35. Feature: Deals on apples Scenario: 3-for-2 on apples Given a

    pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 3 packs of apples Then they should be requested to pay $5.00 Scenario: 3-for-2 deal reminder Given a pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 2 packs of apples Then they should be requested to pay $5.00 But they should be reminded about the 3-for-2 deal import {actorCalled} from '@serenity-js/core' // TODO: who's managing the shop? // Shopper role: // - finds products of interest // - manages basket // - makes payments actorCalled('Shopper') Jan Molak | serenity-js.org Screenplay Pattern: Actors and roles
  36. Feature: Deals on apples Scenario: 3-for-2 on apples Given a

    pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 3 packs of apples Then they should be requested to pay $5.00 Scenario: 3-for-2 deal reminder Given a pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 2 packs of apples Then they should be requested to pay $5.00 But they should be reminded about the 3-for-2 deal import {actorCalled} from '@serenity-js/core' // Shopkeeper role: // - manages stock // - manages prices // - manages deals actorCalled('Shopkeeper') // Shopper role: // - finds products of interest // - manages basket // - makes payments actorCalled('Shopper') Jan Molak | serenity-js.org Screenplay Pattern: Actors and roles
  37. Feature: Deals on apples Scenario: 3-for-2 on apples Given a

    pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 3 packs of apples Then they should be requested to pay $5.00 Scenario: 3-for-2 deal reminder Given a pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 2 packs of apples Then they should be requested to pay $5.00 But they should be reminded about the 3-for-2 deal import {actorCalled} from '@serenity-js/core' // Shopkeeper role: // - manages stock // - manages prices // - manages deals actorCalled('Shopkeeper') // Shopper role: // - finds products of interest // - manages basket // - makes payments actorCalled('Shopper') Jan Molak | serenity-js.org Screenplay Pattern: Actors and roles
  38. Feature: Deals on apples Scenario: 3-for-2 on apples Given a

    pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 3 packs of apples Then they should be requested to pay $5.00 Scenario: 3-for-2 deal reminder Given a pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 2 packs of apples Then they should be requested to pay $5.00 But they should be reminded about the 3-for-2 deal import {actorCalled} from '@serenity-js/core' // Shopkeeper goals? actorCalled('Shopkeeper') // Shopper goals? actorCalled('Shopper') Jan Molak | serenity-js.org Screenplay Pattern: Goals
  39. Feature: Deals on apples Scenario: 3-for-2 on apples Given a

    pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 3 packs of apples Then they should be requested to pay $5.00 Scenario: 3-for-2 deal reminder Given a pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 2 packs of apples Then they should be requested to pay $5.00 But they should be reminded about the 3-for-2 deal import {actorCalled} from '@serenity-js/core' // Shopkeeper goals: // - price apples at $2.50 // - enable 3-for-2 deal on apples actorCalled('Shopkeeper') // Shopper goals: // - find apples // - check price // - check missed deal suggestions actorCalled('Shopper') Jan Molak | serenity-js.org Screenplay Pattern: Goals
  40. Feature: Deals on apples Scenario: 3-for-2 on apples Given a

    pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 3 packs of apples Then they should be requested to pay $5.00 Scenario: 3-for-2 deal reminder Given a pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 2 packs of apples Then they should be requested to pay $5.00 But they should be reminded about the 3-for-2 deal import {actorCalled} from '@serenity-js/core' actorCalled('Shopkeeper').attemptsTo( 
 setProductPrice('apples', '$2.50'), enableDeal('3-for-2', 'apples'), 
 ) actorCalled('Shopper').attemptsTo( findProduct('apples'), addProductToBasket('apples', 3), verifyBasketTotal('$5.00'), verifyMissedDeals([ ]), ) Jan Molak | serenity-js.org Screenplay Pattern: Tasks
  41. Feature: Deals on apples Scenario: 3-for-2 on apples Given a

    pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 3 packs of apples Then they should be requested to pay $5.00 Scenario: 3-for-2 deal reminder Given a pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 2 packs of apples Then they should be requested to pay $5.00 But they should be reminded about the 3-for-2 deal import {actorCalled} from '@serenity-js/core' actorCalled('Shopkeeper').attemptsTo( 
 setProductPrice('apples', '$2.50'), enableDeal('3-for-2', 'apples'), 
 ) actorCalled('Shopper').attemptsTo( findProduct('apples'), addProductToBasket('apples', 3), verifyBasketTotal('$5.00'), verifyMissedDeals([ ]), ) Jan Molak | serenity-js.org Screenplay Pattern: Tasks Task names based on your domain
  42. Feature: Deals on apples Scenario: 3-for-2 on apples Given a

    pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 3 packs of apples Then they should be requested to pay $5.00 Scenario: 3-for-2 deal reminder Given a pack of apples costs $2.50 And there’s a 3-for-2 deal on apples When the shopper wants to buy 2 packs of apples Then they should be requested to pay $5.00 But they should be reminded about the 3-for-2 deal import {actorCalled} from '@serenity-js/core' actorCalled('Shopkeeper').attemptsTo( 
 setProductPrice('apples', '$2.50'), enableDeal('3-for-2', 'apples'), 
 ) actorCalled('Shopper').attemptsTo( findProduct('apples'), addProductToBasket('apples', 3), verifyBasketTotal('$5.00'), verifyMissedDeals([ ]), ) Jan Molak | serenity-js.org Screenplay Pattern: Tasks High-level tasks
 unconstrained by
 interface implementation
  43. actorCalled('Shopkeeper').attemptsTo( 
 setProductPrice('apples', '$2.50'), enableDeal('3-for-2', 'apples'), 
 ) actorCalled('Shopper').attemptsTo( findProduct('apples'),

    addProductToBasket('apples', 3), verifyBasketTotal('$5.00'), verifyMissedDeals([ ]), ) Jan Molak | serenity-js.org Implementing Tasks import {Task} from '@serenity-js/core' const setProductPrice = (name, price) => Task.where(`#actor sets price of ${ name } at ${ price }`, 
 // …sub-tasks 
 ) Tasks are composites
 of activities
  44. actorCalled('Shopkeeper').attemptsTo( 
 setProductPrice('apples', '$2.50'), enableDeal('3-for-2', 'apples'), 
 ) Jan Molak

    | serenity-js.org Implementing API Tasks import {Task} from '@serenity-js/core' import {Send, PostRequest} from '@serenity-js/rest' const setProductPrice = (name, price) => Task.where(`#actor sets price of ${ name } at ${ price }`, 
 Send.a(PostRequest.to('/products').with({ name: name, 
 price: price, }), ) Interacting with
 REST APIs
  45. actorCalled('Shopkeeper').attemptsTo( 
 setProductPrice('apples', '$2.50'), enableDeal('3-for-2', 'apples'), 
 ) Jan Molak

    | serenity-js.org Implementing API Tasks import {Task} from '@serenity-js/core' import {Send, LastResponse, PostRequest} from '@serenity-js/rest' import {Ensure, equals} from '@serenity-js/assertions' const setProductPrice = (name, price) => Task.where(`#actor sets price of ${ name } at ${ price }`, 
 Send.a(PostRequest.to('/products').with({ name: name, 
 price: price, }), Ensure.that(LastResponse.status(), equals(200)) ) Composing assertions
 into tasks
  46. import { CallAnApi } from '@serenity-sj/rest' actorCalled('Shopkeeper') .whoCan( CallAnApi.using(axios) )

    .attemptsTo( 
 setProductPrice('apples','$2.50'), enableDeal('3-for-2', 'apples'), 
 ) Jan Molak | serenity-js.org Implementing API Tasks import {Task} from '@serenity-js/core' import {Send, LastResponse, PostRequest} from '@serenity-js/rest' import {Ensure, equals} from '@serenity-js/assertions' const setProductPrice = (name, price) => Task.where(`#actor sets price of ${ name } at ${ price }`, 
 Send.a(PostRequest.to('/products').with({ name: name, 
 price: price, }), Ensure.that(LastResponse.status(), equals(200)) ) Abilities enable interactions
  47. import { CallAnApi } from '@serenity-sj/rest' actorCalled('Shopkeeper') .whoCan( CallAnApi.using(axios) )

    .attemptsTo( 
 setProductPrice('apples','$2.50'), enableDeal('3-for-2', 'apples'), 
 ) Jan Molak | serenity-js.org Implementing API Tasks import {Task} from '@serenity-js/core' import {Send, LastResponse, PostRequest} from '@serenity-js/rest' import {Ensure, equals} from '@serenity-js/assertions' const setProductPrice = (name, price) => setProductPriceViaAdminApi(name, price) const setProductPriceViaAdminApi = (name, price) => Task.where(`#actor sets price of ${ name } at ${ price }`, 
 Send.a(PostRequest.to('/products').with({ name: name, 
 price: price, }), Ensure.that(LastResponse.status(), equals(200)) )
  48. import { CallAnApi } from '@serenity-sj/rest' actorCalled('Shopkeeper') .whoCan( CallAnApi.using(axios) )

    .attemptsTo( 
 setProductPrice('apples','$2.50'), enableDeal('3-for-2', 'apples'), 
 ) Jan Molak | serenity-js.org Alternative implementations import {Task} from '@serenity-js/core' import {Send, LastResponse, PostRequest} from '@serenity-js/rest' import {Ensure, equals} from '@serenity-js/assertions' const setProductPrice = (name, price) => setProductPriceViaAdminApi(name, price) const setProductPriceViaAdminApi = (name, price) => // … const setProductPriceViaAdminUI = (name, price) => Task.where(`#actor sets price of ${ name } at ${ price }`, authenticateWithStoreAdminPanel(adminCredentials()), openProductCatalog(), findProductByName(name), changeProductPrice(price), 
 ) Alternative implementations of the same task
  49. import { BrowseTheWebWithWebdriverIO } from '@serenity-sj/webdriverio' actorCalled('Shopkeeper') .whoCan( CallAnApi.using(axios), BrowseTheWebWithWebdriverIO

    
 .using(browser) ) .attemptsTo( 
 setProductPrice('apples','$2.50'), enableDeal('3-for-2', 'apples'), 
 ) Jan Molak | serenity-js.org Alternative implementations import {Task} from '@serenity-js/core' import {Send, LastResponse, PostRequest} from '@serenity-js/rest' import {Ensure, equals} from '@serenity-js/assertions' const setProductPrice = (name, price) => setProductPriceViaAdminUI(name, price) const setProductPriceViaAdminApi = (name, price) => // … const setProductPriceViaAdminUI = (name, price) => Task.where(`#actor sets price of ${ name } at ${ price }`, authenticateWithStoreAdminPanel(adminCredentials()), openProductCatalog(), findProductByName(name), changeProductPrice(price), 
 ) The same test scenario, interacting through the web UI
  50. import { BrowseTheWebWithWebdriverIO } from '@serenity-sj/webdriverio' actorCalled('Shopkeeper') .whoCan( CallAnApi.using(axios), BrowseTheWebWithWebdriverIO

    
 .using(browser) ) .attemptsTo( 
 setProductPrice('apples','$2.50'), enableDeal('3-for-2', 'apples'), 
 ) Jan Molak | serenity-js.org Implementing web UI tasks import {Task} from '@serenity-js/core' import {Enter, Click, Text} from '@serenity-js/web' import {Ensure, equals} from '@serenity-js/assertions' const authenticateWithStoreAdminPanel = ({username, password}) => Task.where(`#actor authenticates with StoreAdmin`, Enter.theValue(username).into(LoginForm.usernameField()), Enter.theValue(password).into(LoginForm.passwordField()), Click.on(LoginForm.submitButton()), Ensure.that(Text.of(AdminPanel.user()), equals(username)) ) Web interactions
  51. import { BrowseTheWebWithWebdriverIO } from '@serenity-sj/webdriverio' actorCalled('Shopkeeper') .whoCan( CallAnApi.using(axios), BrowseTheWebWithWebdriverIO

    
 .using(browser) ) .attemptsTo( 
 setProductPrice('apples','$2.50'), enableDeal('3-for-2', 'apples'), 
 ) Jan Molak | serenity-js.org Implementing web UI tasks import {Task} from '@serenity-js/core' import {Enter,Click,Text,PageElement,By} from '@serenity-js/web' import {Ensure, equals} from '@serenity-js/assertions' const authenticateWithStoreAdminPanel = ({username, password}) => Task.where(`#actor authenticates with StoreAdmin`, Enter.theValue(username).into(LoginForm.usernameField()), Enter.theValue(password).into(LoginForm.passwordField()), Click.on(LoginForm.submitButton()), Ensure.that(Text.of(AdminPanel.user()), equals(username)) ) const LoginForm = { usernameField: () => PageElement.located(By.css('input.username')) .describedAs('username field) passwordField: // … } Locating page elements
  52. Jan Molak | serenity-js.org Blended testing like a pro •

    Acceptance tests focus on actors, goals, and tasks import {actorCalled} from '@serenity-js/core' await actorCalled('Shopkeeper').attemptsTo( 
 setProductPrice('apples', '$2.50'), enableDeal('3-for-2', 'apples'), 
 ) await actorCalled('Shopper').attemptsTo( findProduct('apples'), addProductToBasket('apples', 3), verifyBasketTotal('$5.00'), verifyMissedDeals([ ]), )
  53. Jan Molak | serenity-js.org Blended testing like a pro •

    Acceptance tests focus on actors, goals, and tasks • Tasks provide alternative implementations and enable code reuse import {actorCalled} from '@serenity-js/core' await actorCalled('Shopkeeper').attemptsTo( 
 setProductPrice('apples', '$2.50'), enableDeal('3-for-2', 'apples'), 
 ) await actorCalled('Shopper').attemptsTo( findProduct('apples'), addProductToBasket('apples', 3), verifyBasketTotal('$5.00'), verifyMissedDeals([ ]), ) Serenity/JS Custom
 task libraries findProduct 
 setProductPrice 
 enableDeal Tasks
  54. Jan Molak | serenity-js.org Blended testing like a pro •

    Acceptance tests focus on actors, goals, and tasks • Tasks provide alternative implementations and enable code reuse System
 Under Test Acceptance
 Tests System
 State Serenity/JS
 Modules Custom
 task libraries Targeted
 Tests
  55. Jan Molak | serenity-js.org Blended testing like a pro •

    Acceptance tests focus on actors, goals, and tasks • Tasks provide alternative implementations and enable code reuse • Standardised patterns System
 Under Test Acceptance
 Tests System
 State Serenity/JS
 Modules Custom Integrations Custom
 task libraries Targeted
 Tests
  56. Jan Molak | serenity-js.org Blended testing like a pro •

    Acceptance tests focus on actors, goals, and tasks • Tasks provide alternative implementations and enable code reuse • Standardised patterns mean consistent reporting