QA or the Highway - Component Testing: Bridging the gap between frontend application (by Z. Hamm)

zjhamm304 185 views 39 slides Jun 21, 2024
Slide 1
Slide 1 of 39
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25
Slide 26
26
Slide 27
27
Slide 28
28
Slide 29
29
Slide 30
30
Slide 31
31
Slide 32
32
Slide 33
33
Slide 34
34
Slide 35
35
Slide 36
36
Slide 37
37
Slide 38
38
Slide 39
39

About This Presentation

These are the slides for the presentation, "Component Testing: Bridging the gap between frontend applications" that was presented at QA or the Highway 2024 in Columbus, OH by Zachary Hamm.


Slide Content

Bridging the gap between frontend applications Component Testing: Zachary J. Hamm / 2024-06-21 QA or the Highway – Columbus, OH

Zachary Hamm Staff Software Engineer Automation architecture, frontend development, and SDLC process improvement Focused in: Songwriting and practicing guitar; athletics; retro + multiplayer gaming; arts + history; many other subjects Things I enjoy : About me Previously: Validic; Nationwide Insuranc e; Huntington National Bank

Today you can Review what web components are 01. Discover component-level testing 02. Determine when and what to test 03. Build a web-app test strategy 04. Purpose

Where we were before Building websites with HTML, CSS, and pure JavaScript

Previous web pattern designs 3 column layout Uses HTML elements directly CSS and layout built only for desktop screens Embedded JavaScript, JQuery, AJAX, Flash for functionality Load everything at once Displays simple content, like text or compressed pictures

Where we are now Creating with components

Building with components Components allow reusability and structuring Frameworks extend base languages including JavaScript, Ruby, or Python Content is much more complex and intensive Dynamic rendering and loading Custom theming, grid layouts and responsive webviews for mobile and other screen sizes State can be individually managed inside of components

“A web page is a simple document displayable by a browser….(it) can embed a variety of different types of resources such as: style information…scripts…(and) media” (Mozilla) Firstly, what is a web page? HTML elements, Interactive functions, including requests, Styling and presentation, Photos, videos, and other media types What are components? - An overview This includes

What are components? - An overview “Web components are a set of web platform APIs that allow you to create new custom, reusable, encapsulated HTML tags to use in web pages and web apps.” (webcomponents.org) Then, what are components? They can also include HTML elements, Interactive functions, including requests, Styling and presentation, Photos, videos, and other media types

What are components? - Creating hierarchy Data fetched in request hooks per component, like these product prices Components can be reused, like this footer Components can be deeply nested in other components Forms control data validation and submission Styling, like active colors, can be updated based on state

Performing automated testing against web applications

Benefits Integrated : it asserts against a testable version of the application in a “real” environment Data moving through the application aligns closely with a production environment Allows for external systems to be connected and utilized Automation frameworks do not need to configure the state environment, but be configured to work within it Drawbacks Test flakiness is harder to predict since it relies on uncontrollable environments and external systems Data management, setup, and cleanup is complex to manage Testing against interactions and validations instead of data flow requires large overhead in test preparation Requires deep knowledge of all systems connected in an environment to write accurate tests Slower to execute End-to-end testing - A comparison

Enter: Component testing

Dom Testing Library (React, Svelte, Angular, and more) WebDriverIO Vue Test Utils Playwright Component-level test frameworks

A basic HTML file is created in which the component can be rendered A customizable mount command is built to render a component with data The dev server is launched locally, typically by the test framework A spec test file is opened or executed Tests begin by rendering a customized component using the mount The test can now interact with and validate against the component How are components able to be tested ? Performing component testing

Example 1: Simple component tests

Example 1 - Simple component tests

Example 2: Complexity with mocks and intercepts

Example 2 - Complexity with mocks and intercepts (1)

Example 2 - Complexity with mocks and intercepts (2)

Drawbacks Requires deep knowledge of the component library as well Setup is more complex around configuring the “mount” command Validating request hooks and subscriptions requires registering intercepts and other mocks Using mocked data requires it to be consistent with the service from which it is provided Benefits Isolated: asserts against individual HTML components rendered in a local dev server Validates individual pieces , like styling, functionality, validation, and individual state management Data can be fully managed and controlled, including hooks and requests Can be executed immediately against new changes to a system Very fast Component Testing - A comparison

What should be tested at a component level?

HTML Rendering HTML element existence Conditional element rendering + responsiveness Text, labels, messages, and other contents What should be tested at a component level? it ( `has a field for "First name"` , function () { cy . get ( '[data-testid="sign-up"]' ) . find ( "form.MuiBox-root" ) . find ( ".MuiFormControl-root" ) . find ( "input#name" ) . should ( "exist" ) ; //Test label cy . get ( '[data-testid="sign-up"]' ) . find ( 'input#name' ) . parents ( ".MuiFormControl-root" ) . find ( "label" ) . should ( "have.text" , "First name" ) ; }) ;

What should be tested at a component level? describe ( 'Checkout Sidebar' , function (){ it ( "adds the shipping cost to the total cost on the last step" , function () { cy . intercept ( "**/products/*" , { fixture : "products/checkout_info/custom-plan-products.json" , }) ; cy . mount ( <Checkout /> ) ; cy . get ( `[data-testid='total-price']` ). should ( "have.text" , "$144.99" ) ; cy . contains ( "button" , "Next" ). click () ; cy . contains ( "button" , "Next" ). click () ; cy . contains ( "button" , "Place order" ). should ( "be.visible" ) ; //Shipping is $9.99, so it should be added to the total cy . get ( `[data-testid='total-price']` ). should ( "have.text" , "$153.98" ) ; }) ; }) Functionality & events Interactions: click, hover, keystrokes, etc. Pre/post event listener updates

What should be tested at a component level? describe ( 'Input form' , function (){ it ( "requires a valid email address" , function () { //... }) ; it ( "requires all fields to be filled" , function () { cy . get ( "input#name" ). clear () ; cy . get ( "input#email" ). clear () ; cy . get ( "input#password" ). clear () ; cy . contains ( "button" , "Sign up" ). click () ; cy . get ( ".Mui-error" ). should ( "have.length.of" , 3 ) ; }) ; }) Data validation Allowable values, list options, and data entry States: Success, error, required, disabled, etc.

What should be tested at a component level? describe ( 'Checkout Sidebar' , function () { it ( "loads product details in the sidebar" , function () { //Arrange: This test uses a custom intercept to load products from a Cypress fixture const fixtureFileName = "/products/checkout_info/custom-plan-products.json" ; cy . intercept ( "**/products/*" , { fixture : fixtureFileName}) ; cy . mount ( <Checkout/> ) ; //Assert: total price is displayed and each product is rendered cy . get ( `[data-testid='checkout-info-section']` ) . find ( `[data-testid='total-price']` ) . should ( "exist" ) ; cy . fixture ( "/products/checkout_info/custom-plan-products.json" ). then ((fixture) => { fixture. map ((product: Products.CheckoutInfo) => cy . assertCheckoutSidebarProduct (product) ) ; }) ; }) ; }) ; State management and hooks Request handling in hooks Data processing using mocks Cookies and storage events

describe ( 'Sign up' , function () { it ( "renders with the theme on light mode on load" , function () { cy . mount ( <SignUp/> ) //Do testing on CSS here }) ; it ( "can be toggled between light and dark modes" , function () { cy . mount ( <SignUp/> ) cy . get ( 'button[aria-label="Theme toggle button"]' ). click () ; //Do testing on CSS here }) ; }) ; What should be tested at a component level? Styling and theming CSS identifiers and classes Colors, modifiers, SVG paths Visual testing and responsiveness

Styling and theming CSS identifiers and classes Colors, modifiers, SVG paths Visual testing + responsiveness State management and hooks Request handling in hooks Data processing using mocks Using cookies and storage events Data validation Allowable values, list options, and data entry States: Success, error, required, disabled, etc. Functionality & events I nteractions: click, hover, keystrokes, etc. Pre/post event listener updates What should be tested at a component level? HTML Rendering HTML element existence Conditional element rendering + responsiveness Text, labels, messages, and other contents

Customizing the mount command cy.mount() This example uses Cypress.io . Stores & models Plugins CSS, themes, styles, & fonts Intercepts, routers, & stubs Global components

Page object hierarchy “Component object” “Page object” Usable inside class AddressForm { elements = { container : () => cy . get ( 'form#address' ) , firstNameField : () => this . elements . container () . find ( 'input#first-name' ) , lastNameField : () => this . elements . container () . find ( 'input#last-name' ) , addressLine1 : () => this . elements . container () . find ( 'input#address-1' ) , //... } } class CheckoutPage { components = { AddressForm : (handlerFn) => cy . root () . within (handlerFn( new AddressForm())) } fillInAddress (data) { this . components . AddressForm (component => { component . elements . firstNameField . type (data.firstName) ; //... }) } }

Putting it all together : A comprehensive test plan

Putting it all together - A comprehensive test plan Validate functions, services, and components in place Requires extra configuration, but is atomic and controllable Unit & component testing Integration (page-level) testing End-to-end testing Validate the web application by itself, controlling and intercepting data where possible Check state management between pages Validate the application and its connections Data originates from actual sources and ends in actual targets Difficult to manage, but mimics production

??? Well, kinda. Group question: “Can a page also be a component?”

Putting it all together - Is a page a component? Does it render all necessary components? Are there conditional cases for when components should render? By intercepting and mocking data, are the data placed in the components correctly? Does it have links that can be asserted upon? Is it responsive based on desktop and mobile views? Test a page as a component by asking: Does this work in the context of the application correctly? Does it satisfy the overall business need or feature requirements? Can data from another page or source be used and submitted on this page correctly? Does local storage work for cookies, authentication sessions, and cache? Is it secure? Test a page as end-to-end by asking:

Live demo! https://github.com/hammzj/component-test-presentation-demo

Summary Separates reusable pieces of a webpage into individual components Contain their own data, functionality, styling, and state Web components Like “unit testing” mounted pieces of a web page Component-level testing Application test strategy Highly customizable and fast Occurs closer to development, usually as test-driven development Aligns test objects closely with HTML code Can be built to contain other components Unit testing exists for functions and services Component-level testing for web component functionality Integration-level testing for isolated application state management End-to-end testing for data management into external systems

Thanks!

References Mozilla , What is the difference between web page, website, web server, and search engine? WebComponents.org , Introduction Testing Library , dom-testing-library Vue Test Utils Cypress.io , Cypress Component Testing WebDriverIO Playwright , Components (experimental) ViewComponent.org , Testing Rails Applications Web Design Museum.org , early websites Amazon.com , website picture references are respective of Amazon Wikipedia , (for logos) Google , Fonts Icons PPTMON.com , Theme Zachary Hamm , Presentation demo on Github

Statement All names, organizations, and locations are fictional and do not represent real people. All relations are coincidental and not done with intent.