Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
上手に付き合うコンポーネントテスト
Search
Yosuke Kurami
September 20, 2024
Programming
2.4k
6
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
上手に付き合うコンポーネントテスト
Yosuke Kurami
September 20, 2024
More Decks by Yosuke Kurami
See All by Yosuke Kurami
TypeScript LSP の今までとこれから
quramy
1
2.1k
フロントエンドテストの育て方
quramy
12
3.8k
App Router 悲喜交々
quramy
8
740
Patched fetch did not work
quramy
6
790
GraphQL あるいは React における自律的なデータ取得について
quramy
18
5.9k
Next.js App Router
quramy
15
3.9k
Fragment Composition of GraphQL
quramy
17
4.8k
reg-viz VRT tools
quramy
4
1.7k
NoInfer
quramy
0
390
Other Decks in Programming
See All in Programming
地域 SRE コミュニティ最前線 - ホンマでっかSRE勉強会
tk3fftk
0
230
霧の中の代数的エフェクト
funnyycat
1
390
symfony/aiとlaravel/boost
77web
0
130
20260623_Loop Engineeringで自分の分身の問い合わせBotを作る
ryugen04
0
220
PHP に部分適用が来るぞ!……ところで何それ?おいしいの? #phpcon / phpcon-2026
shogogg
0
210
【やさしく解説 設計編・中級 #1】一つの車に、運転手は一人 ~ある倉庫システムの事例から~
panda728
PRO
0
180
AWS CDK を「作」ってみた 〜フルスクラッチで見えた CDK の裏側〜 / aws-cdk-from-scratch
gotok365
3
400
「正の参照」と 「負の導出」で組む ハーネスエンジニアリング
cottpan
1
140
ITヒヤリハットを整理してみた ~ライフサイクルと原因から考える再発防止策~
koukimiura
1
110
ルールを書いて終わらせないハーネスエンジニアリング
yug1224
2
1.5k
Terraform標準の組織で AWS CDKをどう使うか
mu7889yoon
0
220
AI時代、エンジニアはどう育つのか -未経験エンジニアの成長を間近で見て考えたこと-
thasu0123
0
110
Featured
See All Featured
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
220
SEO for Brand Visibility & Recognition
aleyda
0
4.6k
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
650
What does AI have to do with Human Rights?
axbom
PRO
1
2.2k
Documentation Writing (for coders)
carmenintech
77
5.4k
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
630
HDC tutorial
michielstock
2
750
A Soul's Torment
seathinner
6
3.1k
AI Search: Where Are We & What Can We Do About It?
aleyda
0
7.7k
Between Models and Reality
mayunak
4
370
Bridging the Design Gap: How Collaborative Modelling removes blockers to flow between stakeholders and teams @FastFlow conf
baasie
0
610
Git: the NoSQL Database
bkeepers
PRO
432
67k
Transcript
্खʹ͖߹͏ ίϯϙʔωϯτςετ 2024.09.20 @Quramy
About me - id: @Quramy (GitHub / X ) -
͓ࣄ: Web ϑϩϯτΤϯυΤϯδχΞ - React, Next.js, GraphQL - ՝֎׆ಈ: ςετࢧԉܥπʔϧͷ࡞ - Prisma ORM ؔ࿈: jest-prisma, prisma-fabbrica - Visual Testing ؔ࿈: reg-suit, Storycap
Visual Testing ಉձ https://speakerdeck.com/quramy/reg-viz-vrt-tools?slide=6
Agenda - ίϯϙʔωϯτςετͷಋೖ - ίϯϙʔωϯτςετͷӡ༻
ಋೖฤ
ࣗಈςετͷత - εϐʔυΛଛͳΘͣʹ։ൃΛଓ͚ΔͨΊ - طଘͷػೳ͕ͳ͘ಈ࡞͚͍ͭͮͯ͠Δ͜ͱΛ୲อͯ͘͠ΕΔ - ϑϩϯτΤϯυͷ߹ʮݟ͕ͨͲͷΑ͏ʹมߋ͞ΕΔͷ͔ʯ͕Մࢹ Խ͞ΕΔͱɺϨϏϡΞଆͷ֬ೝίετݮʹͭͳ͕Δ
උ͑େࣄ - ϓϩδΣΫτ্ཱͪ͛࣌ίϯϙʔωϯτςετಋೖΛݟૹΔͷબ ࢶͱͯ͠ΞϦͱࢥ͏ - ͨͩ͠ɺޙ͔ΒಋೖͰ͖ΔΑ͏ʹඋ͓͑ͯ͘͜ͱॏཁ - ϓϩδΣΫτ͕ҭ͔ͬͯΒಥવʮΑ͠ ίϯϙʔωϯτͷ Visual
Testing Ζ͏ʂʯͱٸʹࢥཱ͍ͬͯɺθϩ͔ΒςετέʔεΛॻ͘ͷେม
ίϯϙʔωϯτςετͱ Storybook ίϯϙʔωϯτςετʹ Storybook Λ༻͍͍ͯΔཧ༝ - UI ίϯϙʔωϯτ։ൃج൫ͱͯ͠Չঢ়ଶ (ݸਓతʹଞͷπʔϧʹؤுͬͯ΄͍͕͠ɺStorybook ʹউͭͷ͕૬େมͳঢ়گʹͳ͍ͬͯ
Δͱࢥ͏) - Story ͕ςετέʔεͱͯ͠ྲྀ༻ՄೳͰ͋ΔͨΊɺʮͱΓ͋͑ͣ Story ͚ͩॻ ͘Α͏ʹ͓ͯ͘͠ʯ͕ίϯϙʔωϯτςετͷඋ͑ͱͳΔ - Storybook ͕ࣗಈςετʹؔ͢ΔػೳڧԽʹྗΛೖΕ͍ͯΔ - Jest / Vitest ࿈ܞ (compositeStories), test-runner, Chromatic, etc...
ίϯϙʔωϯτςετ with Storybook Storybook ͱ࿈ܞ͢ΔςετπʔϧςετϓϥοτϑΥʔϜ༷ʑͳͷ ͕͋ΔɻओཁͳͷΛհ (1/4) - Chromatic: https://www.chromatic.com/
Storybook ͷϗεςΟϯάࣗಈςετΛ݉Ͷඋ͑ͨ all-in-one ͳαʔϏ εɻStorybook ΦϑΟγϟϧ - Lost Pixel: https://www.lost-pixel.com/ VRT πʔϧɻChromatic ͷΑ͏ͳ SaaS ͱͯ͠ͷఏڙܗଶͷଞɺࣗલͷ CI ʹηϧϑϗετ͢Δ͜ͱՄೳͳπʔϧ
ίϯϙʔωϯτςετ with Storybook Storybook ͱ࿈ܞ͢ΔςετπʔϧςετϓϥοτϑΥʔϜ༷ʑͳͷ ͕͋ΔɻओཁͳͷΛհ (2/4) - Storycap: https://github.com/reg-viz/storycap
Storybook Λ Puppeteer ͰΫϩʔϧͯ͠ը૾Խ͢Δ CLI reg-suit reg-actions (͜ΕΒ reg-vizͷπʔϧ) ͱΈ߹ΘͤΔ͜ͱͰ ίϯϙʔωϯτ୯Ґͷ Visual Regression Test Λ࣮ݱ͢Δɻ Storybook x VRT ͱͯ͠ฮͷ෦ྨ (2017 ͔Β͋Δ)
ίϯϙʔωϯτςετ with Storybook Storybook ͱ࿈ܞ͢ΔςετπʔϧςετϓϥοτϑΥʔϜ༷ʑͳͷ ͕͋ΔɻओཁͳͷΛհ (3/4) - storybook/test-runner: https://storybook.js.org/docs/writing-tests/test-runner
Storybook ެࣜͷςετϥϯφʔɻࣗલ CI Ͱ Story ͷϨϯμϦϯά Play function Λ࣮ߦՄೳɻ jest-playwright Λ෦తʹར༻͍ͯ͠Δ - storycap-testrun: https://github.com/reg-viz/storycap-testrun storybook/test-runner Ͱར༻͢ΔεΫϦʔϯγϣοτϥΠϒϥϦɻ
ίϯϙʔωϯτςετ with Storybook Storybook ͱ࿈ܞ͢ΔςετπʔϧςετϓϥοτϑΥʔϜ༷ʑͳͷ ͕͋ΔɻओཁͳͷΛհ (4/4) - Jest /
Vitest: ୯ମςετͷπʔϧͰ͋Δ͕ɺStorybook ͕ఏڙ͢Δ composeStories ؔͱΈ߹ΘͤΔ͜ͱͰ Story ͷςετ͕Մೳɻ https://storybook.js.org/docs/writing-tests/import-stories-in-tests/ stories-in-unit-tests
͓͜ͱΘΓ - චऀ͕࣮ࡍʹӡ༻ͨ͜͠ͱͷ͋ΔίϯϙʔωϯτςετͷߏҎԼ: - Storybook + Storycap + reg-suit -
Storybook + Jest + compositeStories - Ҏ߱Ͱհ͢Δӡ༻ Tips ɺπʔϧͷબͱಠཱͨ͠༰ͱͳΔΑ͏ۃ ྗ৺͕͚͍ͯ·͕͢ɺ্هΛཹҙͯ͠Β͑Δͱॿ͔Γ·͢
ӡ༻ฤ
ӡ༻ฤ Εʂ ࣮ߦ࣌ؒ
Կ·Ͱ͑ΒΕΔʁ - Pull Request ࣌ͷ CI ࣮ߦ࣌ؒɺͲΕ͘Β͍·ͰզຫͰ͖·͔͢ʁ - ݸਓࠩ͋Δ͕ɺࣗͷ؍ଌൣғͰ 5ลΓʹᮢ͕͋Γͦ͏
- ίϯϙʔωϯτςετͷ࣮ߦ͕࣌ؒ͘ͳΕͳΔ΄ͲɺνʔϜͷϔΠ τ͕ཷ·͍ͬͯ͘
(ࢀߟ) ࣗಈςετ࣮ߦ࣌ؒਪҠ https://qiita.com/Quramy/items/46d0b09ae4d8887b0941 ߦ ίϯϙʔωϯτ έʔε $*࣮ߦ࣌ؒ
ฒྻ࣮ߦ (ϫʔΧʔϨϕϧ) - CI ͷ࣮ߦڥΛCPUΛ૿ڧͭͭ͠ɺ࣮ߦϒϥβ (worker) Λ૿͢ - GitHub Actions
Ͱ͋Εɺ Self Hosted Runner Ͱӡ༻͍ͯ͠Δਓ͚ - ίϯϙʔωϯτςετΛ࣮ߦ͢Δ CLI ͷΦϓγϣϯͰ worker Λมߋ: - Storycap CLI: --parallel Φϓγϣϯ - storybook/test-runner CLI: --max-workers Φϓγϣϯ
ฒྻ࣮ߦ (δϣϒϨϕϧ) - CI ͷ࣮ߦڥ͝ͱฒྻԽ͢Δύλʔϯ - GitHub Actions Ͱ͋Ε matrix
ͳͲͰ ฒྻδϣϒ࣮ߦՄೳ ※ ίϯϙʔωϯτςετ͕Δ·Ͱͷεςοϓ (ڥࣗମͷηοτΞοϓ npm i ͢Δ෦ ͳͲ) ͕େ͖͍ͱɺฒྻԽޮ͕Լ͕ΔͨΊ͕ඞཁ - storybook/test-runner Storycap ͷ --shard ΦϓγϣϯΛར༻͢Δ
ϥϯφʔΛ͍͚Δ - ࣮ϒϥβΛͬͨίϯϙʔωϯτςετ͍ͦͦ - શͯͷίϯϙʔωϯτͷςετͰɺৗʹϒϥβ͕ඞཁͱ͍͏Θ͚Ͱ ͳ͍ͣ (e.g. ϑΥʔϜόϦσʔγϣϯ aria ଐੑͷ֬ೝ)
- jsdom ͰेͳέʔεͰ͋ΕɺJest / Vitest + compositeStories ͷςε τίʔυͱ࣮ͯ͠ߦͰ͖Εɺ͘ͳΓʹ͍͘ - Storycap (= Puppeteer): 100 msec / story - Jest + jsdom: 10 msec / story
࣮ફฤ ௵ͤʂ Flakiness
Flaky Test μϝθολΠ - Flaky ͳςετͱ - ιʔείʔυʹมߋΛՃ͍͑ͯͳ͍ͷʹ ޭ/ࣦഊ ͕҆ఆ͠ͳ͍ঢ়ଶ
- ِཅੑ (False Positive) ࣗಈςετͷఢ - ಛʹ Visual Testing ݕূʹը૾Λ༻͍ΔͨΊ Flaky ςετʹؕΓ͍͢
VRT ʹ͓͚Δᮢͷௐ - ϒϥβʹίϯϙʔωϯτΛඳըͤ͞Δ߹্ɺશͳ pixel by pixel ͷ Ұக͍͠ -
e.g. CSS ͷ border-radius box-shadow ΞϯνΤΠϦΞε - [IMO] 100 ϐΫηϧ (ॎԣ 10px ͷਖ਼ํܗͷ໘ੵ) ఔͷࠩҟڐ༰ - 100 ఔͰِӄੑʹͳΔ͜ͱ໓ଟʹͳ͍ - [IMO] ը૾શମʹର͢ΔൺͰࢦఆ͢ΔͷͰ͋Εɺ 0.01% Λج४ʹ
෦తͳࠩ͠ସ͑ ίϯϙʔωϯτͷҰ෦͕ෆ҆ఆͳ߹ ( e.g. YouTube ຒࠐಈը, iframe) ɺ VRT ࣌ͷΈผ
div ʹࠩ͠ସ͑ͯɺFlaky Test ʹͳΒͳ͍Α͏ʹ͢Δ export function MyComponent() { return ( <div> {/* ͕͜͜ෆ҆ఆͳཁૉ */} <iframe style={{ height: 400, width: "100%" }} src="https://example.com/hogehoge" /> </div> ); } import { isScreenshot } from "storycap"; export function MyComponent() { return ( <div> {process.env.NODE_ENV !== "production" && isScreenshot() ? ( <div style={{ height: 400, background: "#888" }} /> ) : ( <iframe style={{ height: 400, width: "100%" }} src="https://example.com/hogehoge" /> )} </div> ); }
ͦΕͰμϝͳΒ skip - Ͳ͏ͯ͠ Flakiness Λ௵ͤͳ͍߹ɺͦͷέʔε(= Story) Λ ࣗಈςετର ֎ʹׂͯ͠Ε૭ԽΛࢭ͢Δ
- ίʔυΛมߋ͍ͯ͠ͳ͍ͷʹ CI Ͱ VRT ΛΒͤΔͨͼʹ͕ࠩग़ͯ͠· ͏ঢ়گΛ์ஔ͢ΔΑΓϚγ export const MyStory = { args: {}, // storybook/test-runner ͷ߹ tags: ["skip"], parameters: { screenshot: { // Storycap ͷ߹ skip: true, }, }, } satisfies Story;
͓ΘΓʹ - ίϯϙʔωϯτςετӡ༻ͷίπ - Slow Test / Flakey Test ݟա͝͞ͳ͍
- Off topics - Ұൠతͳࣗಈςετϊϋ (e.g. ςετίʔυͷೝෛՙରࡦ) ׆ ༻ͯ͠ɺΑΓշదͳϑϩϯτΤϯυςετΛࢦ͠·͠ΐ͏ - e.g. https://gihyo.jp/list/group/αόϯφศΓ- ~ ιϑτΣΞ։ൃͷ ߥΛੜ͖ൈ͘ ~
Thank you !