Slide 1

Slide 1 text

Storybook ͷ UI Testing Handbook ΛಡΜͩ 2022-01-19 ϑϩϯτΤϯυLTձ - vol.5ɹ#frontendlt

Slide 2

Slide 2 text

ࣗݾ঺հ Shingo Yamazaki • גࣜձࣾϩάϥε • ࡢ೥9݄͔Β University of the People Ͱ 
 ίϯϐϡʔλʔαΠΤϯεษڧத zaki-yama zaki___yama

Slide 3

Slide 3 text

͸͡Ίʹ

Slide 4

Slide 4 text

ϑϩϯτΤϯυͷςετʹର͢Δ೰Έ • ϑϩϯτΤϯυͷςετɺԿΛͲ͜·Ͱॻ͍ͨΒ͍͍΋ͷ͔… • Ͳ͏͍͏؍఺ͰςετΛॻ͍ͨΒ͍͍ͷ͔ • ࠷ۙ͸Visual Regression Testingͱ͔Α͘ฉ͘ɻͦΕҎ֎͸…ʁ • ൺֱతมߋ͕ੜ͡΍͍͢UIͷςετΛͲ͜·Ͱॻ͘΂͖͔ • Ͳ͏͍͏πʔϧʗϥΠϒϥϦΛ࢖͏ͷ͕͍͍ͷ͔ • etc. 🤔

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

UI Testing Handbook • Storybook ͕ఏڙ͍ͯ͠ΔνϡʔτϦΞϧίϯςϯπͷͻͱͭ • ࡢ೥12݄ʹެ։͞Εͨ • Twilio, Adobe, Shopify ͳͲɺStorybookίϛϡχςΟͷ10ݸͷνʔϜ ΛϦαʔνͯ͠ಘΒΕͨ஌ݟΛ·ͱΊͨ΋ͷʢ”Introduction” ΑΓʣ

Slide 7

Slide 7 text

Handbook ͷ಺༰Λ ͬ͘͟Γ঺հ͠·͢

Slide 8

Slide 8 text

αϯϓϧΞϓϦέʔγϣϯ • Α͋͘ΔTodoΞϓϦ • ΞϓϦέʔγϣϯଆͷίʔυ͸ 
 ΄΅͍͡Βͣɺ 
 Storybook΍ςετΛॻ͖ͳ͕Β 
 ֶΜͰ͍͘ߏ੒

Slide 9

Slide 9 text

Introduction • ݱࡏͷओཁͳJavaScriptϑϨʔϜϫʔΫ͸͍ͣΕ΋ίϯϙʔωϯτυϦϒϯ • Unit, Integration, E2E ͱ͍ͬͨ෼ྨͰ͸ͳ͘ɺUI͕࣋ͭಛ௃(characteristics)ʹϑΥʔΧε ͠·͠ΐ͏

Slide 10

Slide 10 text

ςετ͢΂͖UIͷಛ௃ • Visual • ݟͨ໨ • Interaction • ΫϦοΫ΍ϢʔβʔೖྗͳͲͷΠϕϯτ͕ద੾ʹϋϯυϦϯά͞ΕΔ͔ • Accessibility • ΞΫηγϏϦςΟ • User fl ow • ෳ਺ͷίϯϙʔωϯτʹ·͕ͨͬͯ׬݁͢ΔϢʔβʔૢ࡞͕ظ଴௨Γߦ͑Δ͔

Slide 11

Slide 11 text

ͦΕͧΕΛ࣮ݱ͢ΔͨΊͷπʔϧʗϥΠϒϥϦ • Visual • ݟͨ໨ … Chromatic • Interaction … Jest & Testing Library • ΫϦοΫ΍ϢʔβʔೖྗͳͲͷΠϕϯτ͕ద੾ʹϋϯυϦϯά͞ΕΔ͔ • Accessibility • ΞΫηγϏϦςΟ … Axe • User fl ow … Cypress (or Playwright, Selenium) • ෳ਺ͷίϯϙʔωϯτʹ·͕ͨͬͯ׬݁͢ΔϢʔβʔૢ࡞͕ظ଴௨Γߦ͑Δ͔

Slide 12

Slide 12 text

Visual • Chromatic Λ࢖ͬͨ Visual Regression Testing ͷ঺հ • Storybook ͷ಺༰Λը૾ͰΩϟϓνϟ͠ɺίϛοτؒͰࠩ෼͕ͳ͍ ͔νΣοΫ͢Δ

Slide 13

Slide 13 text

Visual: Chromatic • Visual Regression TestingΛ؆୯ʹಋೖͰ͖ΔαʔϏε • ࣗલͰ΍Δͱreg-suit + AWS S3ͳͲͷετϨʔδαʔϏεͰߏங •

Slide 14

Slide 14 text

Interaction • Jest & Testing Library (@testing-library/react) • Storybook ͷ Story Λςετέʔεʹ΋࠶ར༻͠·͠ΐ͏

Slide 15

Slide 15 text

Interaction import { render, waitFor, cleanup, within, fireEvent, } from "@testing-library/react"; import { composeStories } from "@storybook/testing-react"; import * as stories from "./InboxScreen.stories"; describe("InboxScreen", () => { const { Default } = composeStories(stories); it("should pin a task", async () => { const { queryByText, getByRole } = render(); await waitFor(() => { expect(queryByText("You have no tasks")).not.toBeInTheDocument(); }); const getTask = () => getByRole("listitem", { name: "Export logo" }); const pinButton = within(getTask()).getByRole("button", { name: "pin" }); fireEvent.click(pinButton); const unpinButton = within(getTask()).getByRole("button", { name: "unpin", }); expect(unpinButton).toBeInTheDocument(); });

Slide 16

Slide 16 text

Interaction import { render, waitFor, cleanup, within, fireEvent, } from "@testing-library/react"; import { composeStories } from "@storybook/testing-react"; import * as stories from "./InboxScreen.stories"; describe("InboxScreen", () => { const { Default } = composeStories(stories); it("should pin a task", async () => { const { queryByText, getByRole } = render(); await waitFor(() => { expect(queryByText("You have no tasks")).not.toBeInTheDocument(); }); const getTask = () => getByRole("listitem", { name: "Export logo" }); const pinButton = within(getTask()).getByRole("button", { name: "pin" }); fireEvent.click(pinButton); const unpinButton = within(getTask()).getByRole("button", { name: "unpin", }); expect(unpinButton).toBeInTheDocument(); }); 4UPSZΛΠϯϙʔτ͠ɺ ͦͷ··ςετέʔεͱͯ͠ར༻͢Δ

Slide 17

Slide 17 text

Accessibility • Axe ͱ͍͏ϥΠϒϥϦΛ࢖͏ͱΞΫηγϏϦςΟΛ͋Δఔ౓ػցతʹ νΣοΫͰ͖Δ • ͞ΒʹStorybook༻ͷΞυΦϯ΍JestͰAxeΛ࢖ͬͨνΣοΫΛ૸Β ͤΔͨΊͷ jest-axe ͱ͍͏ϥΠϒϥϦ͕͋Δ

Slide 18

Slide 18 text

Accessibility: storybook-addon-a11y ΞΫηγϏϦςΟҧ൓Օॴ͕ 4UPSZCPPL্Ͱ֬ೝͰ͖Δ ʢ✅ೖΕΔͱ֘౰Օॴ͕ϋΠϥΠτʣ

Slide 19

Slide 19 text

Accessibility: jest-axe import { axe, toHaveNoViolations } from "jest-axe"; import { composeStories } from "@storybook/testing-react"; import * as stories from "./InboxScreen.stories"; expect.extend(toHaveNoViolations); describe("InboxScreen", () => { ... const { Default } = composeStories(stories); // Run axe it("should have no accessibility violations", async () => { const { container, queryByText } = render(); await waitFor(() => { expect(queryByText("You have no tasks")).not.toBeInTheDocument(); }); const results = await axe(container); expect(results).toHaveNoViolations(); }); UP)BWF/P7JPMBUJPOT Λݺͼग़͚ͩ͢

Slide 20

Slide 20 text

User fl ow • ͍ΘΏΔE2Eςετ • 2ͭͷબ୒ࢶ͕ߟ͑ΒΕΔ 1. όοΫΤϯυ·ͰؚΊͨ׬શͳςετ؀ڥΛ࢖͏ʢO’ReillyνʔϜ͸ͬͪ͜ʣ 2. όοΫΤϯυ͸ϞοΫ͠ɺϑϩϯτΤϯυͷΈʢTwilioνʔϜ͸ͬͪ͜ʣ • νϡʔτϦΞϧͰ͸ޙऀɻCypress Λ࢖͏ • “ϩάΠϯը໘Ͱਖ਼͍͠Ϣʔβʔ໊&ύεϫʔυΛೖྗͨ͠ΒɺλεΫҰཡ͕։͚Δ” ͱ͍͏ςετ έʔε • ͜͜Ͱ΋ Story Λςετέʔεͱͯ͠࠶ར༻

Slide 21

Slide 21 text

·ͱΊ

Slide 22

Slide 22 text

͜͏͍ͬͨ͜ͱֶ͕΂ͯΑ͔ͬͨ☺ • ֤छςετ؍఺ͱͦΕΛ࣮ݱ͢ΔͨΊͷ۩ମతͳϥΠϒϥϦ • Chromatic, Axe, Cypress, etc. • StorybookΛத৺ʹஔ͍࣮ͨ༻తͳϫʔΫϑϩʔ • StoryΛJest΍Cypressͷςετʹ΋࠶ར༻͢Δɺͱ͍͏ߟ͑ํ • GitHub ActionsʹΑΔCIʹ͍ͭͯ΋঺հ͞Ε͍ͯΔʢ”Automate” ͷষʣ • ࣮ࡍʹίʔυॻ͍ͯಈ͘΋ͷΛݟͳ͕Βֶ΂ͨͷ΋ݸਓతʹ͸Α͔ͬͨ

Slide 23

Slide 23 text

Ҿ͖ଓ͖Θ͔ΒΜ😭 • ͦΕͧΕͷςετΛͲ͏͍͏ج४Ͱ૊Έ߹ΘͤͯɺΞϓϦέʔγϣϯશମͱ ͯ͠ͷ඼࣭Λ୲อ͢Δ͔ • ಛʹ Interaction ͱ User fl ow (E2E) ͷ੗Έ෼͚ • “Given this trade-off, most teams use a hybrid approach to balance effort and value. E2E tests are limited to only critical user fl ows and interaction tests are used to verify all other behavior.” • όοΫΤϯυΛϞοΫͨ͠E2Eςετ͸ͳΜͱͳ͘த్൒୺ͳͷͰ͸…ͱ͍ ͏ײ͡΋͢Δ

Slide 24

Slide 24 text

͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ ࣸܦͨ͠ίʔυ͸ https://github.com/zaki-yama-labs/ui-testing-handbook ʹ͋Γ·͢