หมดปัญหาการหา Element ไม่เจอเวลาเขียน Web Automated Test โดยใช้ Eslint Plugin

Traitanit Huangsri
Cypress.io Thailand
5 min readMar 4, 2022

สวัสดีครับ ปัญหาที่ Automation Testers หลายๆ คนมักเคยเจอกันเมื่อเริ่มต้นเขียน Automated Test ให้กับ Web Application ของตัวเองก็คือ พอใส่ Selector เพื่อใช้ในการ Find element แล้วเกิด error ขึ้นว่าหา element นั้นไม่เจอ ซึ่งก็ทำให้เทสข้อนั้นรันแล้ว Failed โดยที่ยังไม่ทันได้เริ่มเทส functional ของ Web App เลยด้วยซ้ำ

มารู้จัก CSS Selectors กันให้ดีก่อน

CSS Selectors คือ Selectors ที่เราใช้ในการเข้าถึง HTML elements ที่ render อยู่ใน Web Page ของเรา ซึ่งก็มีวิธีการเรียกใช้งาน CSS Selectors ในหลากหลายรูปแบบเช่น

1. Type, Class และ ID Selectors

<div class="center mt-0 pr-2">
<span class="center mb-2">Hello Banner</span>
</div>

ลองดูตัวอย่างนี้ครับ ถ้าสมมติเราอยากเข้าถึง <span> element เพื่อเช็คดูว่ามี InnerHTML เป็นค่า Hello Banner อยู่หรือไม่ ส่วนใหญ่แล้ว เราก็มักจะใช้ Class Selector ในการเข้าถึง element นั้น เช่น .center

แต่ปัญหาก็คือ การที่เราใช้ Class Selector แบบ .center นี้จะเห็นได้ว่ามันมีการใช้ CSS Class นี้มากกว่า 1 ที่ นั่นหมายความว่าเวลาเราใช้งานจริงก็ไม่สามารถระบุได้ว่าต้องการจะเข้าถึง <div> หรือ <span> element กันแน่

หลายคนก็อาจจะบอกว่า เราสามารถ combine การใช้งาน CSS Class Selector ร่วมกับ ​Type Selector ก็ได้นี่ (Type Selector คือการ Select Element ด้วยชนิดของ HTML element เช่น input , button, span ) โดยใช้ Selector แบบนี้ span[class=”center”]

แต่ปัญหาก็คือ บางคนอาจจะไม่ทราบว่า CSS Class นั้นมีโอกาสเปลี่ยนแปลงได้ตลอดเวลา เช่นสมมติว่าเมื่อ User มีการกดปุ่มใน Web Page นั้น ตัว App อาจมีการทำ Re-Rendering HTML ใหม่ โดยเปลี่ยน CSS Class ไป เช่นเปลี่ยนสีปุ่มจากเขียวเป็นสีเทา ซึ่งทำให้ค่า CSS Class Selector เดิมที่เคยใช้งานได้ ก็อาจจะใช้งานไม่ได้แล้ว

ดังนั้น CSS Class Selector จึงไม่เหมาะสมที่จะนำมาใช้เขียน Automated Test เพราะค่าของ CSS Class Selector “มีโอกาสเปลี่ยนแปลงได้อยู่ตลอดเวลา” ทำให้เกิดปัญหาเรื่อง Flaky Test ได้ง่าย

แล้ว ID Selector ล่ะ ?

<select id="country-list">
<option>USA</option>
<option>Thailand</option>
<option>Japan</option>
</select>

แน่นอนว่า id selector นั้นดีกว่าการใช้ Class Selector มาก เช่นในกรณีนี้เราสามารถใช้ #country-list ในการเข้าถึง select element ได้

แต่ครับแต่ ถึงแม้ว่า ​ID Selector นั้นจะสามารถทำให้เราเข้าถึง element ต่างๆ ได้ดีขึ้น แต่มันก็มีโอกาสเปลี่ยนแปลงค่าได้โดยพวก Event Dispatcher ต่างๆ อยู่ดี (เช่นเมื่อเกิด User คลิกปุ่มแล้วมีการ Update ค่า ID Attribute) มีเคสแบบนี้ที่เกิดขึ้นบ่อยๆ เลยล่ะครับ ทำให้ ID ที่เราเคยคิดว่าเป็นสิ่งที่เราจะเพิ่งพาได้ ก็จะไม่เสมอไปอีกต่อไป

2. Attribute Selector

Attribute Selector คือ Selector ที่ใช้ในการเข้าถึง Element โดยผ่านการระบุค่าของ Attribute ของ Element นั้นๆ เช่น

<a href="http://localhost/" data-testid="main-link">Home</a>

หนึ่งใน Best Practices ของการเขียน Web Automated Test ที่ดีนั้นก็คือการใช้ Attribute Selector ในลักษณะที่เป็น Custom Data Attribute

Custom Data Attribute คือ HTML attribute ที่เราเป็นคนกำหนดขึ้นมาเอง ไม่มีผลกับการ Render HTML Element นั้นแต่อย่างใด ถูกนำมาใช้เพื่อประโยชน์ในแง่การพัฒนาหรือเทสแอพเท่านั้น

อย่างในตัวอย่างนี้เราก็สามารถใช้ Attribute ​Selector [data-testid="main-link"] เข้าถึง element <a>ได้เลย ซึ่งค่าของ Custom Data Attribute นั้นจะไม่มีวันเปลี่ยนแปลงค่าได้ เพราะมันไม่ได้ผูกอยู่กับ Event Dispatcher ใดๆ อีกทั้งเรายังสามารถออกแบบการระบุค่าให้มันเองเพื่อป้องกันค่าที่ซ้ำกันได้อีกด้วย

อยากใช้ Custom Data Attribute ต้องทำยังไง?

โดยปกติ ถ้าคุณไม่ได้เป็นคนที่เขียนขึ้น Web ขึ้นมาเอง (หมายถึงคุณอาจจะเป็น QA, Automated Tester) คุณก็จะต้องไปบอก Developer คนที่เขียนโค้ดหน้าเว็บแต่ละหน้าให้เค้าอย่าลืมใส่ Custom Data Attribute ที่ตกลงกันไว้มาให้ด้วยล่ะ

ซึ่งก็ดูเหมือนไม่น่ามีปัญหาอะไรใช่มั้ยครับ แต่ก็นั่นล่ะครับ Developer หลายคนก็มักจะหลงลืมการใส่ Custom Data Attribute เข้ามาให้คนที่เขียน Automated Test อยู่ดีเพราะ Custom Data Attribute มันไม่ได้มีผลอะไรกับโค้ดที่เขียนขึ้นมาเลย พูดง่ายๆ ก็คือถึงไม่มีมัน เว็บก็ทำงานได้ปกติเหมือนเดิม

จะทำอย่างไรให้มั่นใจว่ามี Custom Data Attribute ใส่เข้ามาอย่างครบถ้วนล่ะ ?

ปัญหานี้เราสามารถแก้ได้ 2 วิธีครับ

  1. Code Review: ทุกครั้งที่ Developer มีการแก้โค้ดหน้าเว็บเข้ามา ให้ QA หรือคนที่เขียน Automated Test เข้ามารีวิวโค้ดด้วย เพื่อช่วยเช็คว่ามี Custom Data Attribute ใส่เข้ามาตามที่ตกลงกันไว้ครบหรือเปล่า? ถ้าไม่ครบก็ Request Changes ให้ไปแก้ใส่เข้ามาเพิ่ม ซึ่งวิธีนี้ถ้าโค้ดที่แก้มี Changes ที่ไม่เยอะมาก ก็คงจะพอใช้ตาเราช่วยกันเช็คพอไหว แต่ถ้ามี Changes เข้ามาทีละเยอะๆ ก็คงไม่สามารถตรวจเช็คได้ครบอย่างละเอียดอยู่ดี ตาลายสุดๆ
  2. ใช้ Static Code Analysis Tool: แทนที่เราจะมานั่งใช้สายตาเรากรอกเช็คดูโค้ดแต่ละบรรทัดว่าใส่ Custom Data Attribute ใส่มาครบไหม ก็ให้ไปใช้ Static Code Analysis Tool มาช่วยจับให้เราแทนตั้งแต่ตอนเขียนโค้ดเลยว่า Element ใดบ้างที่ควรจะมี Custom Data Attribute ใส่มาให้เสมอ ซึ่งถ้าไม่ใส่มา ตอนที่สร้าง Pull Request ก็อาจจะให้มีการรัน Static Code Analysis Check ดูว่ามีโค้ดส่วนใดบ้างที่ยังไม่มี Custom Data Attribute ซึ่งจะช่วยลดภาระของเราไปได้ แถมยังมีความแม่นยำที่สูงกว่าอีกด้วย

มาใช้ ESLint Plugin เพื่อช่วยเช็ค Custom Data Attribute กันดีกว่า

ESLint คือ Static Code Analysis Tool ฝั่ง Javascript ที่ช่วยตรวจสอบและแก้ไขปัญหาของโค้ดที่อาจเขียนได้ไม่ถูกต้องตามหลักการ รวมถึง Pattern ที่ควรจะเป็น ซึ่งมีประโยชน์มากที่จะช่วยกรอง Common Human Error ที่เกิดขึ้นจากการเขียนโค้ดได้ บางครั้งช่วยป้องกันไม่ให้เกิด Bug ได้อีกด้วย

ซึ่ง ESLint ก็เปิดช่องให้เราสามารถเขียน Plugin เพื่อเพิ่มความสามารถรวมถึง Rules อื่นๆ เพิ่มเติมที่เราอยากจะ Apply ใช้งานกับโค้ดของเราได้ ซึ่งในบทความนี้จะขอแนะนำ Plugin ตัวนึงที่จะช่วยเช็คให้เราได้ว่ามี Custom Data Attribute อยู่ใน Element ที่เราต้องใช้ในการเขียนเทสหรือไม่

Plugin ที่ว่านั้นมีชื่อว่า “eslint-plugin-test-selectors” ผมลองใช้งานร่วมกับ Web ที่เขียนด้วย React แล้วพบว่าใช้งานได้ดีมาก เลยอยากจะนำมาแชร์ให้ได้ลองใช้กันครับ

วิธีการใช้งานก็ง่ายๆ ดังนี้

  1. Install ESLint และเพิ่ม ESLint Configuration เข้าไปในโปรเจ็ค ในกรณีที่โปรเจ็คของคุณมีการติดตั้งและ Configure ESLint ไว้อยู่แล้วสามารถข้าม Step นี้ไปได้เลย
sh$ npm install eslint --save-dev
sh$ npm init @eslint/config // init eslint config

2. Install eslint-plugin-test-selectors เข้าไปใน Project

sh$ npm install eslint-plugin-test-selectors --save-dev

3. หลังจากนั้นให้ทำการเปิด ESLint Configuration File ขึ้นมา เช่น .eslintrc.js หรือ .eslintrc.json หรือถ้าใครที่ใส่ไว้ใน package.json ก็ให้ดู section ที่เป็น eslintConfig ให้ทำการเพิ่ม plugin test-selectors เข้าไป และในส่วนที่เป็น extends ให้กำหนด default rule ของ test-selectors เป็น plugin:test-selectors/recommended แบบนี้ครับ

โดยค่ามาตรฐาน (default) ของ Plugin ตัวนี้จะคอยเช็ค Element anchor , button , input ว่ามีการใส่ attribute data-test-id ไว้หรือไม่ และรวมถึง Element อื่นๆ ที่มีการทำ Event Listener เช่น onClick, onSubmitซึ่งถ้าลืมใส่มันก็จะขึ้น warning เตือนแบบนี้ครับ

ซึ่งเราสามารถเปลี่ยนแปลง Rule นี้ให้จาก warning เป็น error แทนได้ โดยเปลี่ยนค่า configure ของ ESLint ตรง extends เป็น plugin:test-selectors/recommendedWithErrors แบบนี้

ESLint Error เมื่อลืมใส่ attribute data-test-id

นอกจากนี้เรายังสามารถที่จะ Custom ชื่อ Data Attribute ของเราเองได้ด้วย ในกรณีที่เราไม่ใช้ชื่อ attribute ว่า data-test-id เช่นสมมติว่าอยากเปลี่ยนเป็น data-atid ก็สามารถ config ที่ rules ของ ESLint ได้แบบนี้ครับ

Custom เปลี่ยนชื่อ Attribute เองได้

ในตัวอย่างนี้ ผมต้องการให้ element <button> ทุกอันจะต้องใส่ custom data attribute ชื่อว่า data-atid เข้ามาเสมอ ดังนั้นเมื่อไม่ใส่เข้ามาก็จะเจอ error แบบนี้

ESLint ฟ้องว่าต้องใส่ attribute data-atid เข้ามาด้วยนะ

ซึ่ง Rules ทั้งหมดที่เราสามารถ Custom ได้สามารถดูได้จาก GitHub ของ Plugin ตัวนี้เลย

ในแง่การใช้งานจริง เราอาจจะต้องให้ ESLint ทำงานทุกครั้งเมื่อมีการสร้าง PR ขึ้น และตั้งกฎว่าถ้าเกิด ESLint แล้วเจอ Error ก็จะไม่อนุญาตให้ Merge PR นั้น ก็จะช่วยให้เราสามารถ Detect Human Error ต่างๆ ที่เกิดขึ้นระหว่างเขียนโค้ดไปได้เยอะมากๆ ครับ

เช่นตัวอย่างนี้ ผมลองใช้ GitHub Actions ให้ช่วยรัน ESLint Check ทุกครั้งที่มีการสร้าง PR เกิดขึ้น ผมลองไม่ใส่ค่า Custom Data Attribute เข้าไป ผลที่ได้ก็จะเป็นแบบนี้

Merging is Blocked เพราะว่า ESLint Check ไม่ผ่าน
เมื่อไปดูรายละเอียดก็พบว่าลืมใส่ data-test-id ไว้ใน <button>

สามารถเข้าไปดูตัวอย่าง PR เต็มๆ ได้ที่ลิ้งค์ด้านล่างนี้เลยครับ

Note ไว้ว่า eslint-plugin-test-selectors นี้ใช้งานร่วมกับ Web App ที่เขียนในลักษณะที่เป็น JSX เช่น React เท่านั้นนะครับ ส่วนถ้าใครที่ใช้ Web Framework อื่นๆ เช่น Vue.js ก็มี ESLint ลักษณะคล้ายๆ นี้เช่นกัน สามารถไปลองดูได้จากลิ้งค์ด้านล่างนี้เลยครับ Concept เดียวกัน ผมลองแล้ว Work ดีมากครับ

สรุป

การนำ ESLint ซึ่งเป็น Static Code Analysis Tool เข้ามาช่วยจัดการ Common Problem หรือ Human Error ต่างๆ ที่เกิดจากการเขียนโค้ดนั้นช่วยลดปัญหาที่อาจเกิดขึ้นภายหลังได้มากมาย รวมทั้งยังสามารถที่จะอำนวยความสะดวกให้สามารถเขียน Automated Test ได้ดีขึ้นอีกมากๆ ด้วย

หวังว่าบทความนี้จะเป็นประโยชน์กับ Developers, QA รวมถึง Automated Testers ทุกคนที่จะสามารถนำ Practice นี้ไปปรับใช้กับ Project ของตัวเอง และช่วยลดปัญหาที่เกิดขึ้นในการ Find Elements ต่างๆ เพื่อนำมาใช้เขียน Automated Tests ได้นะครับ Happy Testing!

--

--