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
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
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.6k
How to use Scala.js in real world?
kyo_ago
1
2.2k
Other Decks in Programming
See All in Programming
RTSPクライアントを自作してみた話
simotin13
0
580
Dataformのリポジトリを立ち上げるときにまずやること / dataform-day0-2026
snhryt
0
150
New "Type" system on PicoRuby
pocke
1
830
AutonomyとControlのあいだ:Graflowで記述するAIエージェント協調
myui
0
120
Skillsは効率化、Agentsは"自分の拡張"——Builder時代のエージェント編成(CC Night 2026)
wemra
1
120
Signal Forms: Beyond the Basics @ngBaguette 2026 in Paris
manfredsteyer
PRO
0
240
Vite+ Unified Toolchain for the Web
naokihaba
0
280
JavaDoc 再入門
nagise
0
320
dRuby over BLE
makicamel
2
330
These Five Tricks Can Make Your Apps Greener, Cheaper, & Nicer
hollycummins
0
280
Spec Driven Development | AI Summit Lisbon
danielsogl
PRO
0
180
ユニットテストの先へ:テスト技法で要求・仕様を整理するJava開発実践 / Beyond_Unit_Testing_Practical_Java_Development_Techniques_for_Organizing_Requirements_and_Specifications
shimashima35
0
390
Featured
See All Featured
4 Signs Your Business is Dying
shpigford
187
22k
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
1
350
Build your cross-platform service in a week with App Engine
jlugia
234
18k
The agentic SEO stack - context over prompts
schlessera
0
810
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
390
Building a Modern Day E-commerce SEO Strategy
aleyda
45
9.1k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
2.9k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
The World Runs on Bad Software
bkeepers
PRO
72
12k
Designing for Performance
lara
611
70k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
The Art of Programming - Codeland 2020
erikaheidi
57
14k
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ͱͷ࿈ܞʁ