เริ่มต้นเขียน Unit Tests ให้กับ LIFF App ของคุณด้วย Cypress กัน

Traitanit Huangsri
Sep 12 · 6 min read
Image for post
Image for post

สวัสดีครับ บทความนี้จะขอพาทุกคนไปเริ่มต้นเขียน Unit Tests ให้กับ LIFF (LINE Front-end Framework) App ของคุณด้วย Cypress กันครับ ขอบอกเลยว่าเขียนง่ายมากๆ และสามารถทำตามได้จากตัวอย่างในบทความนี้ได้เลย

สำหรับใครที่เข้ามาอ่านบทความนี้แล้ว ยังไม่เคยลองพัฒนา LIFF App มาก่อน ผมขอแนะนำให้อ่านบทความด้านล่างนี้ก่อนนะครับ เพราะในบทความนี้อาจจะมีการใช้คำศัพท์เทคนิคหลายอย่างที่ต้องการความรู้เบื้องต้นของการพัฒนา LIFF App มาก่อนด้วยครับ

ปัญหาหลักของการทดสอบ LIFF App กับ LINE Platform

ผมเชื่อว่านักพัฒนาแต่ละท่านก็คงจะมีวิธีการทดสอบ LIFF App ของตัวเองที่แตกต่างกันไป จากประสบการณ์การทำ Automated Test ให้กับ LIFF App ของผม ผมได้เจอปัญหาหลายๆ อย่างซึ่งก็คิดว่าคงคล้ายๆ กับนักพัฒนาหลายๆ ท่าน จึงขอสรุปปัญหาที่เจอบ่อยๆ ไว้ 3 ข้อดังนี้

Image for post
Image for post
  1. LIFF App กับ ​LINE Platform มี Dependency ต่อกันสูง: เช่นสมมติว่าเราต้องการเรียก API liff.getProfile() ขึ้นมา เราก็ต้องมีการ redirect ให้ User ไปทำ LINE Login มาก่อนถึงจะใช้งานได้ ซึ่งการควบคุม LINE Login นั้นเป็นสิ่งที่ทำได้ยาก และมีโอกาสทำให้เกิด Flaky Tests ได้ง่าย (Flaky Tests คือเทสที่รันแล้วได้ผลเทสที่ผ่านบ้างไม่ผ่านบ้าง โดยที่ไม่ได้แก้โค้ดอะไรเลย)
  2. ไม่สามารถ Simulate Negative Test ได้เลย: เช่นสมมติว่าเราต้องการจะเทสว่า เมื่อ liff.init() แล้วต้องการให้เกิด Promise Reject เพื่อเช็คว่า App ของเราได้ Handle Scenario นี้ไว้หรือไม่ ผมว่าทำได้ยาก หรือทำไม่ได้เลยดีกว่าในการทำเทสแบบปกตินะครับ
  3. การ Emulate Environment ให้เหมือนกับรัน LIFF App บน LINE Native App บนมือถือทำได้ยาก: บางครั้ง LIFF App ของเราอาจจะมี Behavior ในการใช้งานที่แตกต่างกันไปในแต่ละ Platform เช่นรันบน LINE App กับบน External Browsers บน Desktop อาจจะมีการ Design UI หรือ Function ต่างๆ ที่ทำงานที่แตกต่างกัน ทีนี้เราจะทำให้ยังไงให้สามารถ Emulate การเทสบน Environment ต่างๆ นี้ได้ง่ายๆ ซึ่งผมว่าบางทีมันก็ยากมากเลยนะครับ

จากปัญหาที่ว่ามาข้างต้นทั้งหมด จะสามารถแก้ไขและทำให้เป็นจริงได้ โดยการใช้ Cypress มาเป็น Tool ที่ใช้ทำ Unit Tests ให้กับ LIFF App ของคุณกันครับ

สำหรับใครที่ยังไม่เคยได้ยินหรือรู้จัก Cypress มาก่อน ลองอ่านบทความแนะนำเบื้องต้นที่ผมเคยเขียนไว้ข้างต้นนี้ก่อนก็ได้ครับ

หลายคนอาจจะเข้าใจว่า Cypress มันเหมาะสำหรับการทำ End-to-End Tests อย่างเดียวหรือเปล่า ? คำตอบก็คือ ไม่เลยครับ! เพราะ Cypress สามารถทำเทสได้ทุก Level เลย ตั้งแต่ Unit Test, Integration Test และ End-to-End Test เลยครับ วันนี้ผมจะพาทุกคนไปดูว่าถ้าอยากทำ Unit Test ด้วย Cypress นั้นจะทำได้อย่างไร และมีข้อดีกว่าการใช้ Framework อื่นๆ อย่างไรด้วยครับ

Let’s Get Started

ตัวอย่างในบทความนี้ ผมเลือกที่จะพัฒนา LIFF App ด้วย Vue.js นะครับ

Image for post
Image for post

ผมสร้าง Project ใหม่โดยใช้ vue-cli นะครับ และ Config Project แบบ Manual Select Features แบบนี้ครับ

sh$ vue create liff-cypress-unit-testsPlease pick a preset: Manually select features
Check the features needed for your project: Babel, Router, Linter
Use history mode for router? (Requires proper server setup for index fallback in production) Yes
Pick a linter / formatter config: Airbnb
Pick additional lint features: Lint on save
Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
Save this as a preset for future projects? No

และสิ่งที่สำคัญคือ Cypress ได้สร้าง Plugin ตัวหนึ่งขึ้นมาชื่อว่า Cypress Vue Unit Test Plugin เพื่อมาช่วยให้เราสามารถเขียน Unit Tests บน Vue.js app ได้ง่ายๆ

โดย cypress-vue-unit-test ถูก built on top บน official Vue unit test library อย่าง vue-test-utils แต่นำมาใช้รันเทสบน Browser จริงแถมพ่วงด้วย Feature เจ๋งๆ ของ Cypress ทั้งหมดเลยครับ

โดยเราสามารถ Install Plugin ตัวนี้ผ่าน vue-cli ได้เลยเช่นเดียวกัน ซึ่งการจะใช้งาน Plugin ตัวนี้ได้จะต้องใช้ Cypress 4.5.0 และ Node.js 8 ขึ้นไปด้วยนะครับ

sh$ vue add cypress-experimental # vue-cli > 3

โดย Plugin นี้จะเพิ่ม Cypress เข้ามาใน Project ของคุณอัตโนมัติและสร้าง task test:components รวมถึง Basic Cypress configuration เพื่อให้คุณสามารถรัน Unit Test Vue Components ของคุณได้อย่างง่ายๆ ด้วยครับ

ลองรัน Vue.js App ขึ้นมาก่อน

ผมลองรัน Vue.js App ขึ้นมาก่อนเพื่อดูว่าที่สร้าง Project ไว้นั้นสามารถรันได้ตามปกติ

sh$ yarn serve
Image for post
Image for post
App รันขึ้นมาด้วย Default Template สวยงามครับ

เริ่มพัฒนา Vue Component โดยใช้ LIFF SDK

ผมจะเริ่มทำให้ ​Vue.js App ของผมกลายเป็น LIFF App โดยการ Install LIFF SDK ผ่าน npm package ได้ง่ายๆ แบบนี้เลยครับ

sh$ yarn add @line/liff // LIFF SDK
sh$ yarn add vue-sweetalert2 // for modal dialog

Test Scenario 1: Initialize LIFF App

เริ่มต้นผมจะทำการ Initialize LIFF App ของเราใน App Component ที่ vue-cli สร้างมาไว้ให้เรา โดยผมได้ทำการสร้าง LINE Login Channel และลงทะเบียน LIFF App ไว้ใน LINE Developer Console เป็นที่เรียบร้อยแล้ว

มาเขียน Vue Component Test กันด้วย Cypress

การที่จะเทส Vue Component ด้วย Cypress นั้นก็ง่ายมากๆ ครับ โดยตัว cypress-vue-unit-test ได้เตรียม mount API ไว้ให้เราสามารถ Mount Component ขึ้นมาทำเทสได้เลย และในที่นี้ผมจะใช้ Cypress ในการควบคุมการทำงานของ LIFF APIs ให้เป็นไปตามที่ Test ต้องการ ซึ่งวิธีการก็คือการใช้ Cypress Stub Feature ทำการ Stub liff.init() ให้ return ค่าเป็น promise resolves เสมอ เขียนได้แบบนี้ครับ

นอกจากนี้ผมยังสามารถ Spy ฟังก์ชัน liff.init() ได้ด้วยว่ามีการส่งค่า Parameters ออกไปถูกต้องหรือไม่ ?

หลังจากนั้นเราสามารถรันเทสของเราด้วย Command นี้ครับ

sh$ yarn test:components

Cypress จะทำการ launch UI Test Runner ขึ้นมา

Image for post
Image for post
Cypress UI Test Runner

ให้เราเลือก App.spec.js และกด Run และเราก็จะเห็นว่า Test Passed แล้ว เย้!

Image for post
Image for post

จะเห็นได้ว่าไม่มีการ Test UI ใดๆ เป็นการเทสเพื่อเช็คว่า ถ้า liff.init() success แล้วจะเป็นยังไง เขียนง่ายมากเลยว่ามั้ยครับ

Test Scenario 2: Negative Test with LIFF SDK

ตัวอย่างที่ 2 เราจะมาทดลองการทำ Negative Test กับ LIFF SDK กัน โดยผมจะลองสมมติให้ว่า ถ้าเราเรียก liff.init() แล้วได้ promise Reject แล้ว App ของเราจะ Handle Error ไว้หรือไม่ โดยในโค้ด App.vue ด้านบนผมได้ทำการ try/catch ไว้แล้วว่าถ้า liff.init() ไม่สำเร็จให้ทำการ popup modal อันหนึ่งขึ้นมาเพื่อแสดง error ให้ user เห็นนะครับ

โดยการ Simulate ให้เกิด Negative Test สำหรับเคสนี้คือเราจะใช้ Cypress Stub อีกเช่นเคยทำการเซ็ตให้ liff.init() return ค่าเป็น promise Reject กลับมา ซึ่งสามารถเขียนได้แบบนี้ครับ

เมื่อรันเทสด้วย Cypress ก็จะเห็น Error Modal เด้งขึ้นมาแบบนี้ครับ สวยงาม

Image for post
Image for post

Test Scenario 3: Authentication with LINE Login + Get User Profile

ตัวอย่างนี้ผมจะสร้าง Component ขึ้นมา 1 อันเพื่อใช้สำหรับ Display LINE User Profile โดยผมจะทำการใช้ API liff.getProfile() เพื่อดึงข้อมูลของ User ออกมา ซึ่งการใช้ liff.getProfile() จะ required การทำ LINE Login มาก่อน ดังนั้นใน Component นี้ผมจะทำการเช็คก่อนด้วยครับว่า User ได้ทำการ Login มาแล้วหรือยัง ถ้ายังก็จะให้ Redirect ไปทำ LINE Login มาก่อนโดยใช้ liff.login() นั่นเองครับ

นอกจากนี้ผมยังจะใช้ API liff.ready เพื่อให้ Component รอจนกว่า App จะมีการเรียก liff.init() สำเร็จแล้วก่อนถึงจะเริ่ม Render Profile Component ออกมาครับ

เริ่มเขียนเทสกัน

การเทส Profile Component นั้นผมจะต้องทำการ Stub Property ของ liff.ready ให้เหมือนกับว่าได้มีการทำ liff.init() สำเร็จมาแล้ว ซึ่งนั่นก็คือการทำให้ liff.ready มีค่าเท่ากับ promise resolves นั่นเอง สามารถเขียนได้โดยใช้ Cypress.sinon.replace(liff, 'ready', Promise.resolve()) แบบนี้ได้เลยครับ

นอกจากนี้เราจะทำการ Stub liff.isLoggedIn ให้ return true เสมอ เพื่อทำให้เราไม่ต้องไปทำ LINE Login ให้ยุ่งยาก เสมือนกับว่าเรา Login มาเรียบร้อยแล้วนั่นเอง ที่เหลือก็คือการ Stub liff.getProfile ให้ return promise resolve ด้วยค่าที่เรา Stub ไว้เป็น User Profile แค่นี้ก็เรียบร้อยครับ

เมื่อรันเทสขึ้นมา เราก็จะเห็นเลยว่าเราได้ render Profile component ด้วยค่า User Profile ที่เราทำการ Stub ไว้ และเราก็สามารถทำการ Assert ค่าต่างๆ บน DOM Element ด้วย Cypress Commands ได้เลยครับ

นอกจากนี้เรายังสามารถใช้คำสั่ง Cypress.vue ในการเข้าถึง Vue Instance ได้อีกด้วย! และเราก็สามารถใช้คำสั่งนี้ในการเช็คพวก Property ต่างๆ ที่อยู่ใน Vue Instance ได้เลย เช่นในตัวอย่างนี้ผมต้องการเช็ค Data Property ว่ามันเก็บค่า Object ของ User Profile ถูกต้องหรือไม่ ก็สามารถเรียกใช้ Cypress.vue.$data.<propertyName> ในการเข้าถึงได้เลย เจ๋งมากๆ เลยเนอะ

Image for post
Image for post

Test Scenario 4: Emulate Opening LIFF App from LINE Native App

ตัวอย่างนี้เราจะทำการ Emulate ให้ Environment ของการเปิด LIFF App ของเราให้เหมือนกับการเปิดจากแอพ LINE บนมือถือกัน ซึ่งผมจะใช้ API liff.isInClient() เพื่อเช็คว่ากำลังเปิดอยู่ในแอพ LINE หรือไม่ รวมถึง liff.getOS() เพื่อเช็คดูว่ากำลังเปิดอยู่บน Platform อะไรด้วยครับ

เมื่อเขียน Test เราก็ทำการ Stub ฟังก์ชัน liff.isInClient() ให้ return true เสมอ และให้ liff.getOS() return ค่าเป็น ios ดูครับ

นอกจากนี้ผมยังใช้ cy.viewport(‘iphone-xr’) เพื่อทำการ Emulate ขนาดของหน้าจอให้เหมือนกับรันอยู่บนมือถือ iPhone แบบนั้นเลยครับ ซึ่ง Cypress นั้นได้เตรียม preset ของ Mobile Viewport ไว้ให้เราใช้มากมาย ลองดูเพิ่มเติมได้ที่นี่ครับ

Image for post
Image for post
เมื่อรันเทสจะเห็นว่ามัน Emulate เหมือนรันอยู่บนมือถือให้เลย

เปรียบเทียบ Unit Test Framework อื่นๆ กับ Cypress

Image for post
Image for post

จะเห็นได้ว่า Cypress มีจุดเด่นตรงที่ว่ามันรันเทสบน Browser จริง ทำให้สามารถใช้ Native API ของ Browser ได้ทั้งหมด รวมถึง Feature อื่นๆ ที่ Cypress ทำมาให้เหมาะสมกับการเทสมากๆ เช่น Time Travel, Debuggability ต่างๆ ทำให้ Cypress สามารถสร้าง Unit Test ที่มีความเสมือนจริงมากขึ้นแต่ยังคงความเร็วในการรันเทสได้เร็วมากอยู่

นอกจาก Vue.js แล้ว Cypress ก็ยังรองรับการทำ Unit Test บน Web Framework อื่นๆ ด้วยนะครับ ทั้ง React และ Angularjs ก็สามารถใช้ Cypress ทำ Unit test ได้เช่นกัน

Image for post
Image for post

ตัวอย่าง Source Code ทั้งหมดในบทความนี้สามารถดูได้เต็มๆ ที่ลิ้งด้านล่างนี้เลยนะครับ และสำหรับถ้าใครที่มีคำถามข้อสงสัยเกี่ยวกับการใช้งาน Cypress สามารถเข้าร่วมกลุ่ม Cypress.io Thailand Community เพื่อเข้าไปสอบถามได้เลยนะครับ

ผมหวังว่าบทความนี้จะสร้างไอเดียให้นักพัฒนาทุกคนเริ่มต้นเขียน Unit test ให้กับ LIFF App ของเราเพื่อให้ LIFF App ของเราสามารถ maintain ได้ง่ายในระยะยาวและยังมี Code Quality ที่ดีอีกด้วย ขอให้มีความสุขกับการเขียนเทสทุกคน Happy Testing!

สามารถดูตัวอย่าง Source Code ทั้งหมดของบทความนี้ได้ที่ลิ้งค์ด้านล่างนี้เลยครับ

References:

LINE Developers Thailand

Closing the distance.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store