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
おもらし Resolve
Search
Ryotaro Ko
November 01, 2019
Programming
0
680
おもらし Resolve
Retty Engineer Meetup#1 (
https://retty.connpass.com/event/152101/
) で話した LT の公開用スライドです。
Ryotaro Ko
November 01, 2019
Tweet
Share
More Decks by Ryotaro Ko
See All by Ryotaro Ko
タイ語のサジェストを日本人の俺が頑張った道のり
pikatenor
0
130
Other Decks in Programming
See All in Programming
MCPを使ってイベントソーシングのAIコーディングを効率化する / Streamlining Event Sourcing AI Coding with MCP
tomohisa
0
120
git worktree × Claude Code × MCP ~生成AI時代の並列開発フロー~
hisuzuya
1
590
#QiitaBash MCPのセキュリティ
ryosukedtomita
1
1.4k
High-Level Programming Languages in AI Era -Human Thought and Mind-
hayat01sh1da
PRO
0
800
AIと”コードの評価関数”を共有する / Share the "code evaluation function" with AI
euglena1215
1
170
テストから始めるAgentic Coding 〜Claude Codeと共に行うTDD〜 / Agentic Coding starts with testing
rkaga
13
4.9k
Claude Code + Container Use と Cursor で作る ローカル並列開発環境のススメ / ccc local dev
kaelaela
10
6k
“いい感じ“な定量評価を求めて - Four Keysとアウトカムの間の探求 -
nealle
2
11k
Rubyでやりたい駆動開発 / Ruby driven development
chobishiba
1
740
Advanced Micro Frontends: Multi Version/ Framework Scenarios @WAD 2025, Berlin
manfredsteyer
PRO
0
280
AIエージェントはこう育てる - GitHub Copilot Agentとチームの共進化サイクル
koboriakira
0
600
PostgreSQLのRow Level SecurityをPHPのORMで扱う Eloquent vs Doctrine #phpcon #track2
77web
2
540
Featured
See All Featured
RailsConf 2023
tenderlove
30
1.1k
A Modern Web Designer's Workflow
chriscoyier
695
190k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
126
53k
How STYLIGHT went responsive
nonsquared
100
5.6k
The Pragmatic Product Professional
lauravandoore
35
6.7k
We Have a Design System, Now What?
morganepeng
53
7.7k
The Invisible Side of Design
smashingmag
301
51k
The Power of CSS Pseudo Elements
geoffreycrofte
77
5.9k
Statistics for Hackers
jakevdp
799
220k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
7
740
GraphQLとの向き合い方2022年版
quramy
49
14k
Art, The Web, and Tiny UX
lynnandtonic
299
21k
Transcript
+4ςετςΫχοΫ CZ3ZPUBSP,P!QJLBUFOPS ʙ͓Β͠1SPNJTFͷճʙ
͜Ε3FUUZ&OHJOFFS.FFUVQͰͨ͠-5ͷެ։༻ࢿྉͰ͢ IUUQTSFUUZDPOOQBTTDPNFWFOU
͓લ୭ͩ 3FUUZגࣜձࣾ8FCݕࡧνʔϜΤϯδχΞ 3ZPUBSP,P *%େମ!QJLBUFOPS େֶଔۀޙೖࣾ ۀྖҬ όοΫΤϯυ ݕࡧ࣭վળ &MBTUJDTFBSDIͷ͓कΓ
Πϯϑϥख͍ 0ODBMM
͜Μͳςετ "XFTPNFͳϑΥʔϜ ૹ৴ ॲཧͷྲྀΕ
͜Μͳςετ ϐΧνϡ ૹ৴ ॲཧͷྲྀΕ
͜Μͳςετ ϐΧνϡ ૹ৴ ॲཧͷྲྀΕ ೖྗྃޙɺૹ৴ϘλϯΛԡ͢ ˣ
͜Μͳςετ ϐΧνϡ ૹ৴ ॲཧͷྲྀΕ ೖྗྃޙɺૹ৴ϘλϯΛԡ͢ ˣ Ϙλϯ͕άϨʔΞτ͢Δ<BUUSEJTBCMFE> ˣ
͜Μͳςετ ϐΧνϡ ૹ৴ ॲཧͷྲྀΕ ೖྗྃޙɺૹ৴ϘλϯΛԡ͢ ˣ Ϙλϯ͕άϨʔΞτ͢Δ<BUUSEJTBCMFE> ˣ "1*௨৴ ˣ
"1*αʔόʔ
͜Μͳςετ ϐΧνϡ ૹ৴ ॲཧͷྲྀΕ ೖྗྃޙɺૹ৴ϘλϯΛԡ͢ ˣ Ϙλϯ͕άϨʔΞτ͢Δ<BUUSEJTBCMFE> ˣ "1*௨৴ ˣ
ྃͨ͠Βݩʹ͢<BUUS> "1*αʔόʔ
͜Μͳςετ ϐΧνϡ ૹ৴ ॲཧͷྲྀΕ ೖྗྃޙɺૹ৴ϘλϯΛԡ͢ ˣ Ϙλϯ͕άϨʔΞτ͢Δ<BUUSEJTBCMFE> ˣ "1*௨৴ ˣ
ྃͨ͠Βݩʹ͢<BUUS> "1*αʔόʔ ૹ৴Ϙλϯ͕ԡ͞Ε͔ͯΒ௨৴ྃ·Ͱ disabledͰ͋Δ͜ͱΛςετ͍ͨ͠
ίϯϙʔωϯτͷίʔυྫ <template> <form> <input v-model="name" placeholder="Awesome Form"/> <button type="submit" :disabled="loading">ૹ৴</button>
</form> </template> <script> import { callAPI } from 'API௨৴͢Δͭ'; export default { data() { return { name: '', loading: false, }; }, methods: { async onSubmit() { this.loading = true; try { await callAPI(this.name); this.$emit('success'); } catch (error) { // ͳΜ͔Δ } finally { this.loading = false; } }, }, }; </script>
ίϯϙʔωϯτͷίʔυྫ <template> <form> <input v-model="name" placeholder="Awesome Form"/> <button type="submit" :disabled="loading">ૹ৴</button>
</form> </template> <script> import { callAPI } from 'API௨৴͢Δͭ'; export default { data() { return { name: '', loading: false, }; }, methods: { async onSubmit() { this.loading = true; try { await callAPI(this.name); this.$emit('success'); } catch (error) { // ͳΜ͔Δ } finally { this.loading = false; } }, }, }; </script> BTZODBXBJU
Γ͍ͨςετ describe('onSubmit', () => { it('activates loading flag when form
submitted', () => { // before submit expect(wrapper.vm.loading).toBe(false); // waiting API call expect(wrapper.vm.loading).toBe(true); // after submission completed expect(wrapper.vm.loading).toBe(false); }); });
Γ͍ͨςετ describe('onSubmit', () => { it('activates loading flag when form
submitted', () => { // before submit expect(wrapper.vm.loading).toBe(false); // waiting API call expect(wrapper.vm.loading).toBe(true); // after submission completed expect(wrapper.vm.loading).toBe(false); }); }); ͜͜Ͳ͏͢Μͷ
ωλΒ͠ ͓Β͠1SPNJTF1SPNJTFͷSFTPMWFϋϯυϥͷείʔϓΛ֎ʹ࿙Β͢ 3FTPMWF
1SPNJTFSFTPMWF
Promise.resolve(value) • valueͰ3FTPMWF͞ΕͨPromiseΛฦ͢ • ͭ·Γ͜͏ function rizorubu(value) { return new
Promise(function(resolve){ resolve(value); }) } 1SPNJTFSFTPMWF
function rizorubu(value) { return new Promise(function(resolve){ resolve(value); }) }
let leakedResolve; function rizorubu(value) { return new Promise(function(resolve){ leakedResolve =
resolve; }) } 3FTPMWFͷ࿙Β͔ͨ͠
let leakedResolve; function rizorubu(value) { return new Promise(function(resolve){ leakedResolve =
resolve; }) } 3FTPMWFϋϯυϥ͕ είʔϓ֎ʹ࿙ΕΔ 3FTPMWFͷ࿙Β͔ͨ͠
let leakedResolve; function rizorubu(value) { return new Promise(function(resolve){ leakedResolve =
resolve; }) } leakedResolve(Promise.resolve("poyo")); BTZODGVODUJPOrezorubu()Λ ͖ͳͱ͖ʹ3FTPMWFͰ͖Δʂ 3FTPMWFͷ࿙Β͔ͨ͠ 3FTPMWFϋϯυϥ͕ είʔϓ֎ʹ࿙ΕΔ
Γ͍ͨςετ describe('onSubmit', () => { it('activates loading flag when form
submitted', () => { // before submit expect(wrapper.vm.loading).toBe(false); // waiting API call expect(wrapper.vm.loading).toBe(true); // after submission completed expect(wrapper.vm.loading).toBe(false); }); });
ίϯϙʔωϯτͷίʔυྫ <template> <form> <input v-model="name" placeholder="Awesome Form"/> <button type="submit" :disabled="loading">ૹ৴</button>
</form> </template> <script> import { callAPI } from 'API௨৴͢Δͭ'; export default { data() { return { name: '', loading: false, }; }, methods: { async onSubmit() { this.loading = true; try { await callAPI(this.name); this.$emit('success'); } catch (error) { // ͳΜ͔Δ } finally { this.loading = false; } }, }, }; </script>
ίϯϙʔωϯτͷίʔυྫ <template> <form> <input v-model="name" placeholder="Awesome Form"/> <button type="submit" :disabled="loading">ૹ৴</button>
</form> </template> <script> import { callAPI } from 'API௨৴͢Δͭ'; export default { data() { return { name: '', loading: false, }; }, methods: { async onSubmit() { this.loading = true; try { await callAPI(this.name); this.$emit('success'); } catch (error) { // ͳΜ͔Δ } finally { this.loading = false; } }, }, }; </script>
݁ہ͜͏ͳͬͨ let apiResolve; jest.mock('API௨৴͢Δͭ', () => ({ callAPI: () =>
new Promise(resolve => { apiResolve = resolve; }), })); describe('AwesomeForm', () => { let wrapper; beforeEach(() => { wrapper = shallowMount(AwesomeForm); }); +FTUͱWVFUFTUVUJMTΛ͍ͬͯ·͢ describe('onSubmit', () => { it('activates loading flag when form submitted', async () => { // before submit expect(wrapper.vm.loading).toBe(false); const submissionPromise = wrapper.vm.onSubmit(); // waiting API call await wrapper.vm.$nextTick(); expect(wrapper.vm.loading).toBe(true); apiResolve(Promise.resolve(true)); await submissionPromise; // after submission completed expect(wrapper.vm.loading).toBe(false); }); }); });
݁ہ͜͏ͳͬͨ let apiResolve; jest.mock('API௨৴͢Δͭ', () => ({ callAPI: () =>
new Promise(resolve => { apiResolve = resolve; }), })); describe('AwesomeForm', () => { let wrapper; beforeEach(() => { wrapper = shallowMount(AwesomeForm); }); +FTUͱWVFUFTUVUJMTΛ͍ͬͯ·͢ describe('onSubmit', () => { it('activates loading flag when form submitted', async () => { // before submit expect(wrapper.vm.loading).toBe(false); const submissionPromise = wrapper.vm.onSubmit(); // waiting API call await wrapper.vm.$nextTick(); expect(wrapper.vm.loading).toBe(true); apiResolve(Promise.resolve(true)); await submissionPromise; // after submission completed expect(wrapper.vm.loading).toBe(false); }); }); }); jest.mock Ͱ Λஔ͖͑ import { callAPI } from 'API௨৴͢Δͭ';
݁ہ͜͏ͳͬͨ let apiResolve; jest.mock('API௨৴͢Δͭ', () => ({ callAPI: () =>
new Promise(resolve => { apiResolve = resolve; }), })); describe('AwesomeForm', () => { let wrapper; beforeEach(() => { wrapper = shallowMount(AwesomeForm); }); describe('onSubmit', () => { it('activates loading flag when form submitted', async () => { // before submit expect(wrapper.vm.loading).toBe(false); const submissionPromise = wrapper.vm.onSubmit(); // waiting API call await wrapper.vm.$nextTick(); expect(wrapper.vm.loading).toBe(true); apiResolve(Promise.resolve(true)); await submissionPromise; // after submission completed expect(wrapper.vm.loading).toBe(false); }); }); }); +FTUͱWVFUFTUVUJMTΛ͍ͬͯ·͢ jest.mock Ͱ Λஔ͖͑ import { callAPI } from 'API௨৴͢Δͭ'; ͓Β͠
݁ہ͜͏ͳͬͨ let apiResolve; jest.mock('API௨৴͢Δͭ', () => ({ callAPI: () =>
new Promise(resolve => { apiResolve = resolve; }), })); describe('AwesomeForm', () => { let wrapper; beforeEach(() => { wrapper = shallowMount(AwesomeForm); }); describe('onSubmit', () => { it('activates loading flag when form submitted', async () => { // before submit expect(wrapper.vm.loading).toBe(false); const submissionPromise = wrapper.vm.onSubmit(); // waiting API call await wrapper.vm.$nextTick(); expect(wrapper.vm.loading).toBe(true); apiResolve(Promise.resolve(true)); await submissionPromise; // after submission completed expect(wrapper.vm.loading).toBe(false); }); }); }); +FTUͱWVFUFTUVUJMTΛ͍ͬͯ·͢ MPBEJOHUSVF Λݕূͯ͠3FTPMWF ΦέΦέΦοέʔ
Promise.reject(reason) • reasonʹΑΓ3FKFDU͞ΕͨPromiseΛฦ͢ • ɹɹ Έ͍ͨʹॻ͘͜ͱͰΤϥʔ࣌ͷςετͰ͖·͢ 1SPNJTFSFKFDU leakedResolve(Promise.reject(new Error("something happened")))
References: Mozilla. "Promise.reject()". MDN Web docs. 2019/3/23. https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject Mozilla. "Promise.resolve()".
MDN Web docs. 2019/3/18. https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve azu. "Javascript Promise ͷຊ". Ver 1.6.5. https://azu.github.io/promises-book/