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
karmaを使ったSPA向けE2Eテスト技法
Search
kyo_ago
February 27, 2017
Programming
5.7k
6
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
karmaを使ったSPA向けE2Eテスト技法
kyo_ago
February 27, 2017
More Decks by kyo_ago
See All by kyo_ago
フロントエンドの リソース管理の話 TechFeed Summit#1 #techfeed #techfeedsummit
kyo_ago
5
2k
TypeScriptでType Match的なことをする話 #すえなみチャンス暑気払い
kyo_ago
1
1.4k
WebReplayから見るWeb開発の未来 #builderscon
kyo_ago
2
1k
今日から始めるbugbounty
kyo_ago
0
320
E2Eという名称の指すもの
kyo_ago
0
2.7k
How to use Scala.js in real world?
kyo_ago
1
2.2k
Other Decks in Programming
See All in Programming
為什麼你並不需要ViewModel / No, you don't need a ViewModel
lovee
1
480
継続モナドとリアクティブプログラミング
yukikurage
3
680
仕様駆動開発の消費期限
watany
7
2.7k
改善しないと、タスクが回らない。 “てんこ盛りポジション” を引き継いだ情シスの、入社3ヶ月の業務改善録
krm963
0
250
使用 Meilisearch 建立新聞搜尋工具
johnroyer
0
220
AIエージェントで 変わるAndroid開発環境
takahirom
2
770
『コードを書く以外の』エンジニアリング〜課金基盤移行プロジェクト推進のためのTips4選
yuriko1211
0
570
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
480
Claude Team Plan導入・ガイド
tk3fftk
0
250
Laravel Boostに学ぶ、AIにPHPを書かせる技術 〜OSSの実装から蒸留するエージェント制御の王道〜
kentaroutakeda
3
660
Claude CodeとAgentCore Gatewayを繋ぐ際の認証認可 / Authentication and authorization when connecting Claude Code with AgentCore Gateway
har1101
1
180
Terraform標準の組織で AWS CDKをどう使うか
mu7889yoon
1
470
Featured
See All Featured
Docker and Python
trallard
47
4.1k
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
3
400
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.8k
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
200
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
400
Believing is Seeing
oripsolob
1
180
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
610
How People are Using Generative and Agentic AI to Supercharge Their Products, Projects, Services and Value Streams Today
helenjbeal
1
260
Heart Work Chapter 1 - Part 1
lfama
PRO
8
36k
For a Future-Friendly Web
brad_frost
183
10k
Transcript
karmaΛͬͨSPA͚ E2Eςετٕ๏
ࣗݾհ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ ɾ@kyo_ago ɾChatworkͷதͷਓ ɾझຯLocal Proxy࡞ ɾUnder30͡Όͳ͍ΤϯδχΞ ɾਪ͠APIApplication Cache
·ͣँࡑ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ࠓճ௨Γ͕͍͍ͱࢥͬͯʮ&&ςετʯͬͯݴͬͯ·͕͢ *OUFHSBUJPOςετͬͯݴ͏ํ͕ਖ਼֬ͩͬͨΓ͠·͢ɻ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ࠓճ͍͑ͨ͜ͱ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
41"ͷςετʹ 8FC%SJWFS͔ͳ͍ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
8FC%SJWFSͱ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
4FMFOJVN͔Βൃలͨ͠πʔϧ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ϒϥβ͝ͱͷυϥΠόͱ ͦͷυϥΠόͱ௨৴͢Δ"1* LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ ChromeDriver FirefoxDriver IEDriver SafariDriver JSON Wire Protocol
"1*ʹରԠͨ͠ΫϥΠΞϯτΛ ͏͜ͱͰ൚༻తͳςετ͕Ͱ ͖Δ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ྺ࢙͋Δ&&ςετͷ మ൘πʔϧ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ 2004։ൃ։࢝
Ͱͳͥʮ41"ͷςετʹ 8FC%SJWFS͔ͳ͍ʯͷ͔ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
8FC%SJWFS͋͘·Ͱը໘ ΛભҠ͍ͯ͘͠ʮ8FCαΠτʯ ͷͨΊͷςετπʔϧ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
41"͚ͷςετπʔϧͰͳ ͍ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
41"Λ8FC%SJWFSͰςετ͢ Δͷ͍͠ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
8FC%SJWFS௨ৗͷ։ൃπʔ ϧͱҧ͏֓೦Ͱಈ͘ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ ChromeDriver FirefoxDriver IEDriver SafariDriver JSON Wire Protocol ಠࣗϓϩτίϧͷੈք
௨ৗͷ։ൃϑϩʔͱͷ ࠩҟ͕େ͖͍ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ྫ͑ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
/PEFDPOUFYUͱ #SPXTFSDPOUFYUͷஅ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ Electron͕ղܾ͔ͨͬͨͭ͠
let webdriver = require('selenium-webdriver'); let driver = new webdriver.Builder()
.forBrowser('chrome') .build(); driver.executeScript('return 2').then((res) => { console.log('returned ', res); }); driver.get('https://www.google.co.jp/').then(() => { driver.quit(); }); LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ ͚ͩ͜͜#SPXTFSDPOUFYU
%FWFMPQQFS5PPMTͱڝ߹ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ DriverʹΑͬͯ
DPOTPMFMPHͷ༰͕UFSNJOBM Ͱ͔֬͠ೝͰ͖ͳ͍ ʢ0CKFDUల։ͱ͔ग़དྷͳ͍ʣ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
OPEFJOEFYKT UIFOBCMF8FC%SJWFS1SPYZ\ qPX@ $POUSPM'MPX\ QSPQBHBUF6OIBOEMFE3FKFDUJPOT@USVF BDUJWF2VFVF@ 5BTL2VFVF\ OBNF@5BTL2VFVF
qPX@<$JSDVMBS> UBTLT@<0CKFDU> JOUFSSVQUT@OVMM QFOEJOH@OVMM TVC2@OVMM LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ ͜Ε͕ਏ͍ʂ
ը໘ΛભҠ͍ͯ͘͠ʮ8FCα ΠτʯͰ͋ΕͦΕ΄Ͳͳ ͍ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
41"ͷ߹ɺ͜ΕΒͷπʔϧ͕ ͑ͳ͍ͷඇৗʹ͍͠ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
·ͨɺ8FC%SJWFSϖʔδͷ ಡΈࠐΈΫϦοΫޙͷભҠΛ ࣗಈͰͭػೳ͕͋Δ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
var webdriver = require('selenium-webdriver'), By = webdriver.By, until = webdriver.until;
var driver = new webdriver.Builder() .forBrowser('firefox') .build(); driver.get('http://www.google.com/'); driver.findElement(By.name(‘q')).sendKeys('webdriver'); driver.findElement(By.name('btnG')).click(); driver.wait(until.titleIs('webdriver - Google Search'), 1000); driver.quit(); LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ ͬͯ͘ΕΔ
͕ɺ41"Ͱ௨ৗ͜ΕΒͷػೳ ༻͠ͳ͍ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ͦ͜Ͱ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
41"ͷ*OUFHSBUJPOςετʹ ,BSNB LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
,BSNB LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ɾɾɾͬͯ͠·͢ʁ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ࣾͰฉ͍ͯΈͨ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ʮLBSNBͷ͢ΔΜͰ͢Αʯ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ʮ͋Εɺ"OHVMBS+4ઐ༻Ͱ͢Α Ͷʁʯ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ʮ·ͩ͋ͬͨΜͰ͔͢ʁʯ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ʮ1SPUSBDUPSʹͳͬͨͱࢥͬ ͯ·ͨ͠ʯ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ɾɾɾ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ͱͱ"OHVMBS+4ͷςετ༻ ʹ։ൃ͞Εͨςετπʔϧ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ݱࡏී௨ʹ൚༻తͳπʔϧ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ ʢͱݴ͏͔Ή͠ΖAngularͱͷ૬ੑྑ͘ͳ͍Α͏ͳɻɻɻʣ
·ͩ·ͩ։ൃ׆ൃ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ ʢ5લʹver 1.5.0ެ։ʣ
1SPUSBDUPSͱҧ͏ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ ʢProtractorWebDriverܥʣ
ͬ͘͟Γ͍͏ͱ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ϒϥβ্Ͱಈ͘ 6OJUςετϥϯφʔ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
describe('Assert', () => { it('should true', () => { expect(true).toEqual(true);
}); }); LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ ͜Ε͕ϒϥβ্Ͱಈ͘
ࠓճ7FSͰՃ͞Εͨ ػೳΛͬͯ*OUFHSBUJPOςε τͷํΛհ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
DVTUPN$POUFYU'JMF LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ςετΛࢦఆ͞ΕͨIUNM্Ͱ ߦ͏ػೳ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
<!DOCTYPE html> <html lang="ja"> <head> <script src="context.js"></script> <script type="text/javascript"> %CLIENT_CONFIG%
window.__karma__.setupContext(window); %MAPPINGS% </script> %SCRIPTS% <title>Inside Frontend</title> </head> <body> <script type="text/javascript"> window.__karma__.loaded(); </script> </body> </html> LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ ͜͜ʹLBSNBDPOGKTͷ'JMF͕ల։͞ΕΔ ͔͜͜Β࣮ߦΛ։࢝
·ΔͰαΠτ্Ͱ6OJUUFTUΛ Β͍ͤͯΔ͔ͷΑ͏ʹॻ͚Δ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
describe('Document title', () => { it('should be', () => {
expect(document.title).toEqual('Inside Frontend'); }); }); LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
Β͍͘ʂ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
͔͠͠ɻɻɻ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
DVTUPN$POUFYU'JMFʹ Γͳ͋͞Δ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
Γͳ͞ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ಡΈࠐΈॴ͕MPDBMIPTU LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ Γͳ͞
࣮ࡍςετ͍ͨ͠υϝΠϯͰಡ Έࠐ·ΕΔΘ͚Ͱͳ͍ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ ͜͏͍ͨ͠
ௐͯΈͨ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ɻɻɻͪΐͬͱແཧͬΆ͍ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ ͜͏͢Δ
LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ผυϝΠϯ௨৴ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ϒϥβΛͩ·͢ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
खॱ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
௨৴ͷॻ͖͑ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
-PDBMQSPYZΛ͏ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
$ npm install --save-dev karma-proxy-plugin LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
customContextFile: "custom-context-file.html", proxyValidateSSL: false, client: { clearContext: false }, middleware:
['proxy'], proxy: { '/inside-frontend/': { 'target': 'http://inside-frontend.com/', 'changeOrigin': true } }, proxyReq: (proxyReq, req, res, options) => { }, proxyRes: (proxyRes) => { }, LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ LBSNBDPOGKT 44-1SPYZ࣌ͷূ໌ॻΤϥʔΛແࢹ ςετ࣮ߦ͝ͱʹڥΛΫϦΞ͠ͳ͍ LBSNBQSPYZQMVHJO༻ LBSNBඪ४ͷQSPYJFTͰͳ͍͜ͱʹҙ ૹ৴Πϕϯτ ड৴Πϕϯτ
JOTJEFGSPOUFOEͷ௨৴Λ IUUQJOTJEFGSPOUFOEDPN సૹ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
௨৴ͷ͖ઌͷมߋ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
IUNM+4ͷ௨৴ͷ͖ઌΛ JOTJEFGSPOUFOEʹ͚͍ͨ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ 1ͰProxyϓϩηε্ཱ͕͕͚ͪͬͨͩͰɺ ϒϥβͷProxyઃఆΛߦͬͨΘ͚Ͱͳ͍
let base = document.createElement('base'); base.href = '/example/'; document.head.insertAdjacentElement('afterbegin', base); LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
DVTUPNDPOUFYUpMFIUNM
XMLHttpRequest = () => {/* … */}; fetch = ()
=> {/* … */}; LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ DVTUPNDPOUFYUpMFIUNM
؆୯ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ଞʹ͍Ζ͍Ζ͍ͨ͠߹ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
3FGFSFSΛॻ͖͍͑ͨ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
proxyReq: (proxyReq, req, res, options) => { proxyReq.setHeader('Referer', 'https://example.com/'); },
LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ LBSNBDPOGKT
$PPLJFΛॻ͖͍͑ͨ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
proxyRes: (proxyRes) => { if (proxyRes.headers['set-cookie']) { proxyRes.headers['set-cookie'] = proxyRes.headers['set-cookie'].map((cookie)
=> { // karmahttpͳͷͰsecureΛফ͢ return cookie.replace(/\s*secure;?/i, ''); }) } }, LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ LBSNBDPOGKT
ϦμΠϨΫτઌΛมߋ͍ͨ͠ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
proxyRes: (proxyRes) => { if (proxyRes.headers['location']) { // ϦμΠϨΫτઌΛมߋ proxyRes.headers['location']
= proxyRes.headers['location'].replace(/^https:\/\/ example.com/i, '/example/'); } }, LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ LBSNBDPOGKT
ϩάΠϯ͍ͨ͠ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
fetch('/example/login', { method: 'POST', body: new FormData(document.getElementById('#login')) }).then((response) => response.text()).then((text)
=> { // ϨεϙϯεʹToken͕͋Εมʹอଘ // Cookieอ࣋͞ΕΔ window.__karma__.loaded(); }); LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ DVTUPNDPOUFYUpMFIUNM
͜͏͍ͬͨํ๏Λ͏͜ͱͰผ υϝΠϯͰಡΈࠐ·ΕΔΛ ղফ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
QSPYZ3FRɺQSPYZ3FTͷҾ ҎԼࢀর LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ IUUQTHJUIVCDPNOPEFKJUTVOPEFIUUQQSPYZ
͜͜·ͰདྷΔͱςετΛॻ͘ͷ ඇৗʹ؆୯ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ී௨ʹαΠτ্ͷ%FW5PPMT্ Ͱݕূ͢ΔͷͱมΘΒͳ͍Ϩϕ ϧͰςετ͕ॻ͚Δ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ const assert = require("power-assert"); describe('index page', () =>
{ it('Google map', () => { assert(google.maps); }); it('copyright', () => { assert(document.querySelector('.copyright')); }); });
ඇৗʹ͓͢͢Ί LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
࣮ࡍͬͯΈͯͲ͏͔ͩͬͨ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ςετඇৗʹॻ͖͍͢ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ͨͩɺϒϥβʹىҼ͢Δෆ҆ ఆ͞Ͳ͏͠Α͏ͳ͔ͬͨ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
%0.ͱ͍͏Ҭม͍ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ঢ়ଶͷΫϦΞ͕ࣗͷʹͳ ΔͷͰਏ͍ ʢָʹͳΔ෦͋Δ͚Ͳʣ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ࣗͰ੍ޚͰ͖Δൣғ͕͕Δ ͷخ͍͠ ʢ8FC%SJWFSͷϒϥοΫϘο Ϋεײ͕ͳ͍ʣ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ͦͦ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
8FC%SJWFSड͚ೖΕςετ ͷπʔϧͰ͋ͬͯɺ։ൃऀ͚ πʔϧͰͳ͍ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
։ൃ͠ͳ͕Βςετ͢ΔͷͰ͋ Εผͷํ๏Λݕ౼͢Δํ͕͍ ͍ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
ͨͩɺड͚ೖΕςετͳΒ 8FC%SJWFSͷ΄͏͕ྑ͍͔ ͠Εͳ͍ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
8FC%SJWFSߴੑೳ͚ͩͲɺ ෆཁͳػೳଟ͍ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
࠷ޙʹ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
&&ςετ ॻ͔ͳ͍ʹӽͨ͜͠ͱͳ͍ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
࣭͝ʁ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏
࣭͝ʁ LBSNBΛͬͨ41"͚ͷ&&ςετٕ๏ ྫ ɾ࣮ߦ࣌ؒʁ ɾ࣮ࡍͲ͏͍͏;͏ʹಈ͔ͯ͠Δʁ ɾଞͷϝϯόʔͷԠʁ ɾͬͯΑ͔ͬͨʁ ɾͲ͏͍͏ͱ͖ʹ͓͢͢Ίʁ ɾCIͱͷ࿈ܞʁ