ศิลปะแห่งการทดสอบเค้าโครงด้วย Galen Framework
เผยแพร่แล้ว: 2022-03-10เมื่อออกแบบส่วนต่อประสานกราฟิกกับผู้ใช้มักมีคำถามเปิดอยู่เสมอ: เราจะทำการทดสอบอัตโนมัติได้อย่างไร และ เราจะแน่ใจได้อย่างไรว่าเลย์เอาต์ของเว็บไซต์ยังคงตอบสนอง และแสดงผลอย่างถูกต้องบนอุปกรณ์ทุกประเภทที่มีความละเอียดต่างกัน เพิ่มความซับซ้อนที่เกิดขึ้นจากเนื้อหาแบบไดนามิก ข้อกำหนดสำหรับการทำให้เป็นสากลและการแปลเป็นภาษาท้องถิ่น และกลายเป็นความท้าทายที่แท้จริง
ในบทความนี้ ผมจะแนะนำคุณเกี่ยวกับเทคนิคการทดสอบเลย์เอาต์ใหม่ที่น่าสนใจ เมื่อใช้ Galen Framework ฉันจะจัดเตรียมบทช่วยสอนโดยละเอียดสำหรับการเขียนการทดสอบเลย์เอาต์ทั่วไปที่มีความหมาย ซึ่งสามารถดำเนินการได้ในเบราว์เซอร์และทุกอุปกรณ์ และในขณะเดียวกันก็ใช้เป็นแหล่งข้อมูลจริงเพียงแหล่งเดียวในเอกสารการออกแบบของคุณ
อ่านเพิ่มเติม เกี่ยวกับ SmashingMag:
- การพัฒนาด้วยการทดสอบด้วยภาพสำหรับการออกแบบอินเทอร์เฟซที่ตอบสนอง
- พื้นฐานของการทดสอบระบบอัตโนมัติสำหรับแอพ เกม และเว็บบนมือถือ
- กรอบการทดสอบระบบอัตโนมัติที่หลากหลายสำหรับ React Native Apps
ฉันยังจะสาธิตวิธีที่ฉันใช้การทดสอบที่เหมาะสมที่สุดสำหรับหน้าข้อความบนเว็บไซต์คลาสสิฟายด์ของเรา Marktplaats เราจะเรียนรู้วิธีขยายไวยากรณ์ของ Galen ด้วยภาษาของเราเอง วิธีปรับปรุงโค้ดทดสอบ และวิธีเปลี่ยนรูทีนการทดสอบเลย์เอาต์เป็นงานศิลปะ
บทนำสู่กรอบงาน Galen
Galen Framework ครอบคลุมเมื่อปีที่แล้วใน "การพัฒนาที่ขับเคลื่อนด้วยการทดสอบด้วยภาพสำหรับการออกแบบอินเทอร์เฟซที่ตอบสนอง" ในขณะนั้น ไวยากรณ์ของมันถูกจำกัด มันได้รับการปรับปรุงอย่างมากตั้งแต่นั้นมา และได้รับคุณสมบัติใหม่มากมาย ซึ่งเราจะดูที่นี่
ในกรณีที่คุณไม่คุ้นเคยกับ Galen Framework มันเป็นเครื่องมือสำหรับการทดสอบเลย์เอาต์แบบตอบสนองและแบบข้ามเบราว์เซอร์และการทดสอบการทำงานด้วยภาษาทดสอบของตัวเองที่ชื่อว่า Galen Specs มันขึ้นอยู่กับ Selenium WebDriver และยังมี JavaScript API ที่หลากหลายซึ่งช่วยให้คุณทำงานกับ WebDriver ได้โดยตรง เนื่องจากคุณมีสิทธิ์ควบคุม WebDriver คุณจึงทำการทดสอบในเบราว์เซอร์ใดก็ได้ ในคลาวด์ (SauceLabs, BrowserStack, PerfectoMobile ฯลฯ) หรือบนอุปกรณ์มือถือจริงโดยใช้ Appium
การติดตั้งและดำเนินการ
การตั้งค่า Galen Framework เป็นเรื่องง่าย เพียงรันคำสั่งต่อไปนี้เพื่อติดตั้งผ่าน npm:
npm install -g galenframework-cli
หากคุณไม่ได้ใช้ npm เพียงดาวน์โหลดไฟล์เก็บถาวร Galen Framework ล่าสุด แยกแพ็คเกจและทำตามคำแนะนำในการติดตั้ง
เมื่อติดตั้งแล้ว สามารถเปิดใช้ Galen Framework ได้หลายวิธี ตัวอย่างเช่น คุณสามารถใช้คำสั่ง check
เพื่อเริ่มการทดสอบหน้าเดียวอย่างรวดเร็ว สำหรับคำสั่งนี้ คุณต้องจัดเตรียมไฟล์ .gspec
ที่มีการตรวจสอบความถูกต้องของเลย์เอาต์ จากนั้นจึงเรียกใช้ได้ดังนี้
galen check loginPage.gspec --url https://example.com --size 1024x768 --include desktop --htmlreport reports
คำสั่งนี้จะเปิดเบราว์เซอร์ เปิด URL ที่ระบุ ปรับขนาดหน้าต่างเบราว์เซอร์เป็น 1024 × 768 พิกเซล และดำเนินการตรวจสอบทั้งหมดที่ประกาศไว้ในไฟล์ loginPage.gspec
ด้วยเหตุนี้ คุณจะได้รับรายงาน HTML โดยละเอียด
การจัดการชุดทดสอบ
ในโลกแห่งความเป็นจริง เว็บแอปพลิเคชันจริงไม่ได้ประกอบด้วยเพจสแตติกล้วนๆ บ่อยครั้งที่คุณจะต้องดำเนินการบางอย่างเพื่อไปยังสถานที่ที่คุณต้องการตรวจสอบ ในกรณีนี้ Galen ขอเสนอชุดทดสอบ JavaScript และ GalenPages JavaScript API สำหรับการนำโมเดลออบเจ็กต์ของเพจไปใช้ นี่คือตัวอย่างง่ายๆ ของการทดสอบ JavaScript Galen:
test("Home page", function() { var driver = createDriver("https://galenframework.com", "1024x768"); checkLayout(driver, "homePage.gspec", ["desktop"]); });
และนี่คือการนำโมเดลอ็อบเจ็กต์ของเพจไปใช้สำหรับเพจล็อกอิน ที่นำมาจากโปรเจ็กต์จริง
WelcomePage = $page("Welcome page", { loginButton: "#welcome-page .button-login" }); LoginPage = $page("Login page", { username: "input[name='login.username']", password: "input[name='login.password']", loginButton: "button.button-login" loginAs: loggedFunction ("Log in as ${_1.username} with password ${_1.password}", function(user) { this.username.typeText(user.username); this.password.typeText(user.password); this.loginButton.click(); }) }); test("Login page", function() { var driver = createDriver("https://testapp.galenframework.com", "1024x768"); var welcomePage = new WelcomePage(driver).waitForIt(); welcomePage.loginButton.click(); new LoginPage(driver).waitForIt(); checkLayout(driver, "loginPage.gspec", ["desktop"]); });
สำหรับการใช้งานขั้นสูง ผมแนะนำให้คุณดูโครงการ Galen Bootstrap เป็นส่วนขยาย JavaScript ที่สร้างขึ้นโดยเฉพาะสำหรับ Galen มีฟังก์ชันพิเศษบางอย่างสำหรับการทดสอบ UI และวิธีที่ง่ายกว่าในการกำหนดค่าเบราว์เซอร์และดำเนินการชุดทดสอบที่ซับซ้อน
การทดสอบเค้าโครงอย่างง่าย
ให้ฉันเริ่มต้นด้วยการแนะนำการทดสอบเลย์เอาต์อย่างง่ายใน Galen Framework จากนั้น ฉันจะไปยังกรณีการใช้งานขั้นสูงและสาธิตวิธีขยายไวยากรณ์ Galen Specs สำหรับสิ่งนี้ เราจะดูที่ส่วนหัวที่มีไอคอนและคำอธิบายภาพ:

ในโค้ด HTML อาจมีลักษณะดังนี้:
<body> <!-- … --> <div> <img class="header-logo" src="/imgs/header-logo.png"/> <h1>My Blog</h1> </div> <!-- … --> </body>
รูปแบบที่ง่ายที่สุดของการทดสอบเลย์เอาต์ของ Galen จะมีลักษณะดังนี้ ขั้นแรก เราต้องประกาศวัตถุด้วยตัวเลือก CSS
@objects header #header icon #header img caption #header h1
จากนั้น เราประกาศส่วนการทดสอบด้วยชื่อที่สื่อความหมาย และใส่การตรวจสอบทั้งหมดของเราไว้ด้านล่าง
= Icon and Caption = icon: left-of caption 10 to 15px width 32px height 32px inside header 10px top caption: aligned horizontally all header inside header
เราได้ทดสอบองค์ประกอบส่วนหัวสององค์ประกอบ: ไอคอนและคำอธิบายภาพ การตรวจสอบทั้งหมดที่แสดงอยู่ภายใต้องค์ประกอบไอคอนและคำอธิบายภาพนั้นเป็นข้อกำหนดมาตรฐานของ Galen ข้อมูลจำเพาะเหล่านี้เป็นส่วนประกอบพื้นฐานซึ่งคุณสามารถประกอบโซลูชันการทดสอบเลย์เอาต์ของคุณเองได้ ข้อมูลจำเพาะแต่ละรายการจะตรวจสอบคุณสมบัติเดียว (เช่น ความกว้าง ความสูง ข้อความ) ตำแหน่งที่เกี่ยวข้อง (เช่นด้านใน ด้านซ้าย ด้านบน) หรือพิกเซลในภาพหน้าจอ (เช่น แบบแผนชุดสี รูปภาพ)
การทดสอบหลายองค์ประกอบโดยใช้แต่ละลูป
ตัวอย่างก่อนหน้านี้แสดงให้เห็นถึงสถานการณ์สมมติง่ายๆ มาดูกันว่าเราจะจัดการกับสถานการณ์ที่ซับซ้อนกว่านี้ได้อย่างไร: เมนูแนวนอน ขั้นแรก ให้ลองใช้เทคนิคการทดสอบเลย์เอาต์ที่ตรงไปตรงมา

เริ่มต้นด้วยการจับคู่หลายองค์ประกอบบนหน้า ด้วยโค้ดต่อไปนี้ เรากำลังบอกให้ Galen ค้นหาองค์ประกอบที่ตรงกับตัวเลือก CSS #menu ul li
CSS
@objects menu #menu item-* ul li
ต่อมา เราสามารถอ้างถึงรายการเหล่านี้ด้วยชื่อ เช่น menu.item-1
และ menu.item-2
และวนซ้ำรายการเมนูทั้งหมดโดยใช้ @forEach
วนซ้ำ
= Menu = menu.item-1: inside menu 0px top left bottom @forEach [menu.item-*] as menuItem, next as nextItem ${menuItem}: left-of ${nextItem} 0px aligned horizontally all ${nextItem}
อย่างที่คุณเห็น แม้ว่าการตรวจสอบจริงจะไม่ซับซ้อนขนาดนั้น แต่โค้ดนั้นก็ใช้งานง่ายน้อยลงแล้ว ลองนึกภาพถ้าเรามีโค้ดที่คล้ายกันมากกว่านี้ในการทดสอบของเรา เมื่อถึงจุดหนึ่งก็จะกลายเป็นระเบียบที่ไม่สามารถรักษาได้ ควรมีวิธีการปรับปรุง
ทบทวนการทดสอบเค้าโครง
ถ้าคุณนึกถึงตัวอย่างก่อนหน้านี้ ดูเหมือนว่าเราสามารถแสดงเค้าโครงได้ในหนึ่งหรือสองประโยค ตัวอย่างเช่น เราอาจพูดบางอย่างเช่น “รายการเมนูทั้งหมดควรจัดวางในแนวนอนโดยไม่มีระยะขอบระหว่างนั้น รายการเมนูแรกควรอยู่ทางด้านซ้ายของเมนูโดยไม่มีระยะขอบ” เนื่องจากเราได้กำหนดประโยคเพื่ออธิบายเลย์เอาต์ที่เราต้องการ เหตุใดเราจึงใช้พวกมันในโค้ดของเราไม่ได้ ลองนึกภาพว่าถ้าเราสามารถเขียนโค้ดแบบนี้ได้:
= Menu = |first menu.item-* is in top left corner of menu |menu.item-* are aligned horizontally next to each other
อันที่จริง นั่นเป็นรหัสที่ใช้งานได้จริง คัดลอกมาจากโครงการของฉัน ในสองบรรทัดสุดท้าย (เริ่มต้นด้วยไพพ์ |
) เรากำลังเรียกใช้ฟังก์ชันแบบกำหนดเองที่รวบรวมอาร์กิวเมนต์โดยแยกวิเคราะห์สองคำสั่งนั้น แน่นอน ตัวอย่างข้างต้นจะไม่ทำงานตามที่เป็นอยู่ ในการคอมไพล์ เราจำเป็นต้องใช้ตัวจัดการสำหรับสองคำสั่งนี้ เราจะกลับไปที่การใช้งานนี้ในภายหลัง
ประเด็นสำคัญจากตัวอย่างข้างต้นคือการทดสอบเลย์เอาต์ได้ เปลี่ยนจากการทดสอบที่ขับเคลื่อนด้วยอ็อบเจกต์เป็นการทดสอบที่ขับเคลื่อนด้วยนิพจน์ อาจไม่ชัดเจนจากตัวอย่างเล็กๆ น้อยๆ แต่เห็นได้ชัดเจนในสเกลที่ใหญ่ขึ้นอย่างแน่นอน เหตุใดจึงสำคัญ คำตอบสั้น ๆ คือสิ่งนี้เปลี่ยนความคิดของเราและส่งผลต่อวิธีที่เราออกแบบซอฟต์แวร์และเขียนการทดสอบ
เมื่อใช้เทคนิคนี้ เราไม่ถือว่าหน้าเว็บของเราเป็นกลุ่มของออบเจ็กต์ที่มีความสัมพันธ์เฉพาะระหว่างกัน เราไม่ได้ทดสอบคุณสมบัติ CSS ขององค์ประกอบแต่ละรายการ และเราหลีกเลี่ยงการเขียนโค้ดที่ไม่สำคัญที่ซับซ้อน แต่เรากำลังพยายามนึกถึงรูปแบบการจัดวางทั่วไปและข้อความที่มีความหมาย แทนที่จะทดสอบรายการเมนู 1 รายการเมนู 2 และอื่นๆ แยกกัน เราใช้ข้อความทั่วไปที่:
- สามารถทำซ้ำได้ในองค์ประกอบอื่น
- ไม่มีค่าพิกเซลแบบฮาร์ดโค้ด
- ใช้กับสิ่งที่เป็นนามธรรมและไม่ใช้กับวัตถุที่เป็นรูปธรรม
- และสุดท้ายแต่ไม่ท้ายสุด ที่จริงแล้วสมเหตุสมผลเมื่อเราอ่าน
มันทำงานอย่างไร
ให้ฉันอธิบายกลไกของนิพจน์เค้าโครงแบบกำหนดเองโดยใช้ตัวอย่างง่ายๆ นี้:

ในตัวอย่างนี้ เราควรตรวจสอบว่าปุ่มขยายไปถึงแผง โดยไม่มีระยะขอบทางด้านซ้ายหรือขวา หากไม่มีกฎที่กำหนดเอง เราสามารถเข้าถึงได้หลายวิธี แต่ฉันชอบวิธีแก้ปัญหาต่อไปนี้:
button: inside some_panel 0px left right
โค้ดด้านบนช่วยให้เราประกาศระยะขอบแบบกำหนดเองที่ด้านข้างได้อย่างยืดหยุ่น และยังทดสอบโดยปริยายว่าปุ่มนั้นอยู่ภายในแผงควบคุมทั้งหมด ข้อเสียคือไม่สามารถอ่านได้มากนักซึ่งเป็นเหตุผลที่ฉันจะใส่การตรวจสอบนี้ไว้เบื้องหลัง button stretches to some_panel
เพื่อให้ใช้งานได้ เราต้องเขียนกฎที่กำหนดเองดังนี้:
@rule %{elementName} stretches to %{parentName} ${elementName}: inside ${parentName} 0px left right
แค่นั้นแหละ. ตอนนี้เราสามารถทดสอบได้ในบรรทัดเดียว:
| button stretches to some_panel
อย่างที่คุณเห็น กฎนี้ใช้อาร์กิวเมนต์สองอาร์กิวเมนต์: elementName
และ parentName
ทำให้เราสามารถนำไปประยุกต์ใช้กับองค์ประกอบอื่นๆ ได้เช่นกัน เพียงแทนที่ชื่อของวัตถุทั้งสองนั้น
| login_panel stretches to main_container | header stretches to screen | footer stretches to screen # etc.
การใช้ภาษาทดสอบของคุณเอง
กลับไปที่ตัวอย่างเริ่มต้นของนิพจน์เค้าโครงสำหรับเมนูแนวนอน
= Menu = | first menu.item-* is in top left corner of menu | menu.item-* are aligned horizontally next to each other
เราสามารถนำกฎข้อแรกไปใช้ด้วยวิธีต่อไปนี้:
@rule first %{itemPattern} is in %{cornerSides} corner of %{parentElement} @if ${count(itemPattern) > 0} ${first(itemPattern).name}: inside ${parentElement} 0px ${cornerSides}
เมื่อใช้ในตัวอย่างของเรา มันจะแยกวิเคราะห์อาร์กิวเมนต์ดังนี้:
-
itemPattern
=menu.item-*
-
cornerSides
=top left
-
parentElement
=menu
เนื่องจากเราทำนิพจน์แรกเสร็จแล้ว เราจึงสามารถย้ายไปที่นิพจน์ถัดไปได้ ในนิพจน์ที่สอง เราควรทดสอบการจัดแนวตามแนวนอนของรายการเมนูทั้งหมด ฉันเสนอสามขั้นตอนง่ายๆ:
- ค้นหารายการเมนูทั้งหมด
- วนซ้ำทั้งหมดจนถึงองค์ประกอบสุดท้าย
- ตรวจสอบว่าองค์ประกอบ
n
อยู่ทางด้านซ้ายขององค์ประกอบn+1
และขอบด้านบนและด้านล่างอยู่ในแนวเดียวกัน
เพื่อให้มันทำงาน เราต้องใช้ @forEach
loop และ specs left-of
and aligned
โชคดีที่ใน Galen คุณสามารถอ้างถึงรายการก่อนหน้าหรือรายการถัดไปในลูปได้ ในกรณีที่คุณได้ประกาศการอ้างอิงไปยังองค์ประกอบถัดไป มันจะทำซ้ำจนกระทั่งองค์ประกอบสุดท้ายเท่านั้น ซึ่งเป็นสิ่งที่เราต้องการ
@rule %{itemPattern} are aligned horizontally next to each other @forEach [${itemPattern}] as item, next as nextItem ${item}: left-of ${nextItem} 0px aligned horizontally all ${nextItem}
คุณอาจถามว่าถ้าเราต้องระบุระยะขอบในการทดสอบของเรา (เช่น ~ 20px
หรือ 10 to 20px
) จากนั้น ฉันขอแนะนำให้ใช้กฎแยกต่างหากหรือขยายกฎที่มีอยู่เพื่อรองรับอาร์กิวเมนต์ %{margin}
@rule %{itemPattern} are aligned horizontally next to each other with %{margin} margin @forEach [${itemPattern}] as item, next as nextItem ${item}: left-of ${nextItem} ${margin} aligned horizontally all ${nextItem}
แค่นั้นแหละ! เราได้สร้างนิพจน์ทั่วไปที่ช่วยให้เราตรวจสอบเมนูแนวนอนได้ อย่างไรก็ตาม เนื่องจากความยืดหยุ่นของมัน เราสามารถทำได้มากกว่านั้น เราสามารถใช้เพื่อทดสอบองค์ประกอบอื่นๆ บนหน้าได้ เรายังสามารถใช้เพื่อทดสอบการจัดตำแหน่งของปุ่มสองปุ่ม:

| menu.item-* are aligned horizontally next to each other with 0px margin | submit_button, cancel_button are aligned horizontally next to each other with 20px margin
คุณอาจสังเกตเห็นในสองตัวอย่างนี้ว่าเราได้ประกาศอาร์กิวเมนต์แรกในสองวิธีที่แตกต่างกัน ในนิพจน์แรก อาร์กิวเมนต์แรกคือ “menu.item-*”
และในนิพจน์ที่สอง จะถูกประกาศเป็น “submit_button, cancel_button”
นั่นเป็นไปได้เพราะ @forEach
loop ช่วยให้เราใช้รายการของอ็อบเจ็กต์ที่คั่นด้วยเครื่องหมายจุลภาคร่วมกับตัวดำเนินการ star แต่เรายังไม่ได้ทำการปรับโครงสร้างใหม่ เราสามารถปรับปรุงโค้ดเพิ่มเติมและทำให้อ่านง่ายขึ้น หากเราสร้างกลุ่มสำหรับรายการเมนูและปุ่มแบบฟอร์มการเข้าสู่ระบบ เราสามารถทำได้ดังนี้:

@groups menu_items menu_item-* login_form_buttons submit_button, cancel_button = Testing login page = | &menu_items are aligned horizontally next to each other with 0px margin | &login_form_buttons are aligned horizontally next to each other with 20px margin
ในกรณีนี้ เราต้องใช้สัญลักษณ์ &
ซึ่งหมายถึงการประกาศกลุ่ม นั่นเป็นการทดสอบที่ดีอยู่แล้ว ขั้นแรก มันใช้งานได้ และเราสามารถทดสอบสิ่งที่เราต้องการได้ นอกจากนี้ รหัสมีความชัดเจนและสามารถอ่านได้ หากบุคคลอื่นถามคุณว่าหน้าเข้าสู่ระบบควรมีลักษณะอย่างไรและข้อกำหนดในการออกแบบคืออะไร คุณสามารถบอกให้พวกเขาดูการทดสอบได้
อย่างที่คุณเห็น การใช้นิพจน์ที่กำหนดเองสำหรับรูปแบบเลย์เอาต์ที่ซับซ้อนนั้นไม่ใช่เรื่องใหญ่ มันอาจจะท้าทายในตอนแรก แต่ก็ยังคล้ายกับกิจกรรมที่สร้างสรรค์
ระยะขอบแบบไดนามิก
มาดูรูปแบบการจัดวางที่หายากอื่นๆ ที่บางครั้งอาจพบในเว็บไซต์ต่างๆ จะเป็นอย่างไรถ้าเราต้องการทดสอบว่าองค์ประกอบต่างๆ มีระยะห่างระหว่างกันเท่ากัน? ลองใช้กฎอื่นสำหรับสิ่งนั้น แต่คราวนี้ใช้ JavaScript ในการใช้งาน ฉันเสนอข้อความดังกล่าว: “box_item-* are aligned horizontally next to each other with equal distance”
. การดำเนินการนี้จะยุ่งยากเล็กน้อยเนื่องจากเราไม่ทราบระยะขอบระหว่างองค์ประกอบต่างๆ และเราไม่สามารถระบุเฉพาะค่าพิกเซลของฮาร์ดโค้ดได้ ดังนั้น สิ่งแรกที่เราต้องทำคือการดึงระยะขอบที่แท้จริงระหว่างองค์ประกอบแรกและองค์ประกอบสุดท้าย

เมื่อได้ระยะขอบแล้ว เราสามารถประกาศใน @forEach
วนซ้ำได้เหมือนกับที่เราทำก่อนหน้านี้ ฉันเสนอให้ใช้กฎนี้โดยใช้ JavaScript API เนื่องจากตรรกะที่จำเป็นนั้นซับซ้อนกว่าในตัวอย่างก่อนหน้านี้ทั้งหมดเล็กน้อย มาสร้างไฟล์ชื่อ my-rules.js
แล้วพิมพ์โค้ดนี้:
rule("%{objectPattern} are aligned horizontally next to each other with equal margin", function (objectName, parameters) { var allItems = findAll(parameters.objectPattern), distance = Math.round(Math.abs(allItems[1].left() - allItems[0].right())), expectedMargin = (distance - 1) + " to " + (distance + 1) + "px"; if (allItems.length > 0) { for (var i = 0; i < allItems.length - 1; i += 1) { var nextElementName = allItems[i + 1].name; this.addObjectSpecs(allItems[i].name, [ "aligned horizontally all " + nextElementName, "left-of " + nextElementName + " " + expectedMargin ]); } } });
ในโค้ดทดสอบของเรา เราจะใช้ดังนี้:
@script my-rules.js # … = Boxes = | box_item-* are aligned horizontally next to each other with equal distance
อย่างที่คุณเห็น ใน Galen Framework เราสามารถเลือกระหว่างสองภาษาเมื่อใช้งานกฎ: Galen Specs และ JavaScript สำหรับนิพจน์ทั่วไป Galen Specs นั้นใช้งานง่ายกว่า แต่สำหรับนิพจน์ที่ซับซ้อน ฉันเลือก JavaScript เสมอ หากคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับกฎ JavaScript โปรดดูเอกสารประกอบ
Galen Extras
หลังจากเล่นกับรูปแบบเลย์เอาต์ต่างๆ มามากพอแล้ว ฉันจึงตระหนักว่ากฎของ Galen ทั้งหมดนี้สามารถนำไปใช้กับโปรเจ็กต์ทดสอบอื่นๆ ได้อย่างง่ายดาย นั่นทำให้ฉันมีความคิดที่จะรวบรวมนิพจน์เค้าโครงที่พบบ่อยที่สุดลงในไลบรารีของตนเอง นั่นคือเหตุผลที่ฉันมาสร้างโปรเจ็กต์ Galen Extras ด้านล่างนี้คือตัวอย่างบางส่วนของความสามารถของห้องสมุดนี้:
| header.icon should be squared | amount of &menu_items should be > 3 | &menu_items are aligned horizontally next to each other | &list_items are aligned vertically above each other with equal distance | every &menu_item is inside menu 0px top and has width > 50px | first &menu_item is inside menu 0px top left | &menu_items are rendered in 2 column table | &menu_items are rendered in 2 column table, with 0 to 1px vertical and 10px horizontal margin | &login_form_elements sides are vertically inside content_container with 20px margin login_panel: | located on the left side of panel and takes 70 % of its width # etc …
ห้องสมุด Galen Extras มีรูปแบบเลย์เอาต์มากมายที่คุณจะพบได้ทั่วไปในเว็บไซต์ และฉันจะอัปเดตต่อไปทันทีที่ค้นพบรูปแบบที่มีประโยชน์ เมื่อตั้งค่าไลบรารีนี้แล้ว ฉันตัดสินใจลองใช้กับโปรเจ็กต์ทดสอบจริง
ทดสอบแอพส่งข้อความ
ปัจจุบัน ฉันทำงานเป็นวิศวกรซอฟต์แวร์ที่ Marktplaats เมื่อถึงจุดหนึ่ง ฉันตัดสินใจที่จะใช้ประสบการณ์ทั้งหมดที่ฉันได้รับกับโครงการจริง ฉันต้องทดสอบหน้าข้อความบนเว็บไซต์ของเรา นี่คือสิ่งที่ดูเหมือน:

ความจริงแล้ว การใช้การทดสอบสำหรับหน้าดังกล่าวดูน่ากลัวสำหรับฉันอยู่เสมอ โดยเฉพาะอย่างยิ่งการทดสอบเลย์เอาต์ แต่ด้วยไลบรารี Galen Extras ที่ใช้งานได้จริง มันค่อนข้างราบรื่น และในไม่ช้าฉันก็สามารถคิดโค้ดนี้ขึ้นมาได้:
@import ../selected-conversation.gspec @groups (message, messages) messenger.message-* first_two_messages messenger.message-1,messenger.message-2 first_message messenger.message-1 second_message messenger.message-2 third_message messenger.message-3 (message_date_label, message_date_labels) messenger.date_label-* first_date_label messenger.date_label-1 second_date_label messenger.date_label-2 = Messages panel = = Messages and Date labels = |amount of visible &message_date_labels should be 1 |first &message_date_label has text is "17 november 2015" |amount of visible &messages should be 3 |&first_two_messages should be located at the left inside messenger with ~ 20px margin |&third_message should be located at the right inside messenger with ~ 20px margin |&messages are placed above each other with 10 to 15px margin |text of all &messages should be ["Hi there!", "I want to buy something", "Hello! Sure, it's gonna be 100 euros"] = Styling = |&first_two_messages should be styled as others message |&third_message should be styled as own message
การแยกช่วงพิกเซล
การทดสอบดูใช้ได้: มีขนาดกะทัดรัดและอ่านง่าย แต่ยังห่างไกลจากความสมบูรณ์แบบ ฉันไม่ชอบคำจำกัดความระยะขอบเหล่านั้นทั้งหมด ( ~ 20px
, 10 to 15px
) บางคนซ้ำแล้วซ้ำเล่า และเป็นการยากที่จะเข้าใจว่าพวกเขาแต่ละคนยืนหยัดเพื่ออะไร นั่นคือเหตุผลที่ฉันตัดสินใจซ่อนทุกระยะขอบหลังตัวแปรที่มีความหมาย
# ... @set messages_side_margin ~ 20px messages_vertical_margin 10 to 15px = Messages panel = = Messages and Date labels = |amount of visible &message_date_labels should be 1 |first &message_date_label has text is "17 november 2015" |amount of visible &messages should be 3 |&first_two_messages should be located at the left inside messenger with ${messages_side_margin} margin |&third_message should be located at the right inside messenger with ${messages_side_margin} margin |&messages are placed above each other with ${messages_vertical_margin} margin # ...
อย่างที่คุณเห็น ฉันได้ย้ายระยะขอบไปที่ messages_side_margin
และ messages_vertical_margin
ฉันได้ประกาศระยะขอบขั้น minimal
ด้วย ซึ่งเป็นช่วงระหว่าง 0 ถึง 1 พิกเซล
# ... @set minimal 0 to 1px = Conversations Panel = | &conversations are aligned above each other with ${minimal} margin # ...
การตรวจสอบตามรูปภาพและนิพจน์ที่กำหนดเอง
เมื่อครอบคลุมการวางตำแหน่งขององค์ประกอบหลักทั้งหมดบนหน้าแล้ว ฉันจึงตัดสินใจทดสอบการจัดสไตล์ด้วย ฉันต้องการตรวจสอบว่าแต่ละข้อความมีสีพื้นหลังเฉพาะสำหรับบทบาทของผู้ใช้ เมื่อผู้ใช้เข้าสู่ระบบ ข้อความจะมีพื้นหลังสีฟ้าอ่อน ข้อความสำหรับผู้ใช้รายอื่นจะมีพื้นหลังสีขาว ในกรณีที่ไม่ได้ส่งข้อความ การแจ้งเตือนข้อผิดพลาดจะมีพื้นหลังสีชมพู นี่คือกฎที่ช่วยฉันตรวจสอบรูปแบบเหล่านี้:
@set OWN_MESSAGE_COLOR #E1E8F5 OTHERS_MESSAGE_COLOR white ERROR_MESSAGE_COLOR #FFE6E6 @rule %{item} should be styled as %{style} message ${item}: @if ${style === "own"} color-scheme > 60% ${OWN_MESSAGE_COLOR}, 0.2 to 20 % ${MAJOR_TEXT_MESSAGE_COLOR} @elseif ${style === "error"} color-scheme > 60% ${ERROR_MESSAGE_COLOR}, 0.2 to 20 % ${MAJOR_TEXT_MESSAGE_COLOR} @else color-scheme > 60% ${OTHERS_MESSAGE_COLOR}, 0.2 to 20 % ${MAJOR_TEXT_MESSAGE_COLOR}
ข้อมูลจำเพาะ color-scheme
จะตรวจสอบการกระจายตามสัดส่วนของสีภายในองค์ประกอบ มันครอบตัดภาพหน้าจอของหน้าและวิเคราะห์การกระจายสี ดังนั้น ในการตรวจสอบสีพื้นหลังขององค์ประกอบ เราแค่ตรวจสอบว่าการกระจายขององค์ประกอบนั้นมากกว่า 60% ของช่วงสีทั้งหมด ในการทดสอบ กฎนี้เรียกใช้ดังนี้:
= Styling = |&first_two_messages should be styled as others message |&third_message should be styled as own message
การกำหนดค่าชุดทดสอบ
แอปส่งข้อความเป็นแอปพลิเคชันไดนามิกที่ทำงานร่วมกับ RESTful Messaging API ดังนั้น เพื่อทดสอบเลย์เอาต์ในสถานะต่างๆ ทั้งหมด เราจึงต้องเตรียมข้อมูลการทดสอบ ฉันตัดสินใจจำลอง Messaging API เพื่อให้สามารถกำหนดค่าข้อความทดสอบทั้งหมดของฉันในชุดทดสอบได้ นี่คือส่วนย่อยของชุดทดสอบของฉันที่แสดงให้เห็นว่าการทดสอบของเรามีโครงสร้างอย่างไร
// ... testOnAllDevices("Unselected 2 conversations", "/", function (driver, device) { mock.onGetMyConversationsReturn(sampleConversations); refresh(driver); new MessageAppPage(driver).waitForIt(); checkLayout(driver, "specs/tests/unselected-conversations.gspec", device.tags); }); testOnAllDevices("When clicking a conversation it should reveal messages", "/", function (driver, device) { mock.onGetMyConversationsReturn(sampleConversations); mock.onGetSingleConversationReturn(sampleMessages); refresh(driver); var page = new MessageAppPage(driver).waitForIt(); page.clickFirstConversation(); checkLayout({ driver: driver, spec: "specs/tests/three-simple-messages-test.gspec", tags: device.tags, vars: { expectedTextProvider: textProvider({ "messenger.message-1": "Hi there!\n11:02", "messenger.message-2": "I want to buy something\n12:02", "messenger.message-3": "Hello! Sure, it's gonna be 100 euros\n13:02" }) } }); }); // ...
จับแมลง
การใช้นิพจน์ง่ายๆ เหล่านี้ได้ผลค่อนข้างเร็ว มาดูกันว่าเราสามารถตรวจจับข้อบกพร่องประเภทใดได้ด้วยชุดทดสอบของเรา
ปัญหาการจัดแต่งทรงผม
นี่คือตัวอย่างเมื่อมีบางอย่างผิดพลาดในฐานโค้ด CSS ของเรา และด้วยเหตุนี้ ข้อความทั้งหมดจึงแสดงผลด้วยพื้นหลังเดียวกัน

หากคุณเปรียบเทียบภาพหน้าจอนี้กับต้นฉบับ คุณจะสังเกตเห็นว่าข้อความสุดท้ายมีพื้นหลังสีขาว ในขณะที่ควรเป็นสีฟ้าอ่อน มาดูกันว่า Galen รายงานปัญหานี้อย่างไร:

สำหรับวัตถุที่ไฮไลต์ จะแสดงข้อผิดพลาด color #e1e8f5 on “messenger.message-3” is 0% but it should be greater than 60%
ตามจริงแล้ว ข้อความแสดงข้อผิดพลาดนี้ดูไม่ชัดเจน แต่เนื่องจากการตรวจสอบนี้สร้างขึ้นจากกฎที่กำหนดเอง เราจึงสามารถค้นหาชื่อเดิมในสาขารายงานได้เสมอ:

หากคุณเลื่อนขึ้น คุณจะเห็นว่าคำสั่งเดิมคือ &third_message should be styled as own message
นั่นคือข้อดีอีกประการของการใช้นิพจน์ที่กำหนดเอง: ช่วยให้คุณเข้าใจความล้มเหลวและอธิบายการตรวจสอบที่สร้างขึ้นทั้งหมดได้อย่างดี
ปัญหาการวางตำแหน่ง
นี่เป็นอีกตัวอย่างหนึ่งที่เกิดขึ้นเมื่อเลย์เอาต์ผิดพลาดเนื่องจากการจัดตำแหน่งองค์ประกอบไม่ถูกต้อง ในภาพหน้าจอต่อไปนี้ คุณจะเห็นว่าข้อความสุดท้ายอยู่ที่ด้านซ้ายของวิวพอร์ตการส่งข้อความ แทนที่จะเป็นด้านขวา

ดูภาพหน้าจออีกครั้งพร้อมข้อความแสดงข้อผิดพลาด:

ภาพหน้าจอไฮไลต์คอนเทนเนอร์ข้อความและองค์ประกอบข้อความสุดท้าย พร้อมแสดงข้อความแสดงข้อผิดพลาดต่อไปนี้: “messenger.message-3” is 285px right which is not in range of 22 to 28px
นักพัฒนาเว็บอาจไม่ชัดเจนว่าทำไมจึงต้องมีระยะขอบ 22 ถึง 28 พิกเซลทางด้านขวา คุณต้องมองหาคำชี้แจงการตรวจสอบในสาขารายงานอีกครั้ง:

คำสั่งเดิมสำหรับเช็คนั้นคือ &third_message should be located at the right inside messenger with ~ 25px margin
นั่นทำให้รู้สึกมากขึ้น นอกจากนี้ วิศวกรส่วนหน้าคนอื่นๆ จะเข้าใจรายงานการทดสอบนี้ แม้ว่าจะไม่ได้เขียนแบบทดสอบก็ตาม
แนวปฏิบัติการทดสอบเค้าโครง
เมื่อคำนึงถึงการทดลองต่างๆ เหล่านี้แล้ว ฉันจึงตัดสินใจจัดรูปแบบการเรียนรู้ทั้งหมดให้เป็นแนวทางการทดสอบเลย์เอาต์ทั่วไป ต่อไปนี้คือรายการตรวจสอบโดยย่อของขั้นตอนที่ต้องปฏิบัติตามเพื่อลดขั้นตอนการทดสอบของคุณ
- ระบุรูปแบบเค้าโครงในการออกแบบ
- สรุปคำสั่งตรวจสอบความถูกต้อง พยายามย่อส่วนการตรวจสอบส่วนใหญ่ให้เป็นประโยคเดียว
- ส่วนประกอบ! จะดีกว่าเสมอที่จะย้ายการทดสอบขององค์ประกอบที่ทำซ้ำไปยังส่วนประกอบเฉพาะ
- ใช้ชื่อที่มีความหมายสำหรับส่วน กฎ และวัตถุ
- หลีกเลี่ยงพิกเซล พยายามแทนที่ค่าพิกเซล (ไม่ว่าจะเป็นค่าหรือช่วงที่แน่นอน) ด้วยตัวแปรที่มีความหมาย
- ปรับโค้ดของเว็บไซต์ของคุณเพื่อให้ง่ายต่อการทดสอบ ซึ่งจะช่วยคุณจัดโครงสร้างและดูแลทั้งรหัสการผลิตและรหัสทดสอบของคุณ
เกณฑ์การยอมรับในการช่วยเหลือ
บ่อยครั้งฉันได้รับคำถามเช่น “แล้วการทดสอบเลย์เอาต์ควรมีรายละเอียดแค่ไหน? และเราควรทดสอบอะไรเป็นพิเศษ” คำตอบทั่วไปเป็นเรื่องยากที่จะให้ ปัญหาคือเมื่อความครอบคลุมของการทดสอบมีน้อย คุณจะพลาดข้อบกพร่อง ในทางกลับกัน หากคุณมีการทดสอบที่ละเอียดเกินไป คุณอาจได้รับผลบวกที่ผิดพลาดมากมาย และในอนาคต คุณอาจหลงทางในการบำรุงรักษาการทดสอบ ดังนั้นจึงมีข้อแลกเปลี่ยน แต่ฉันได้ค้นพบแนวทางทั่วไปสำหรับตัวเอง หากคุณแบ่งงานออกเป็นเรื่องราวของผู้ใช้ที่มีขนาดเล็กลง การวางโครงสร้างการออกแบบของเพจในรูปแบบของเกณฑ์การยอมรับจะง่ายขึ้น ในท้ายที่สุด คุณอาจใส่เกณฑ์การยอมรับนี้ลงในโค้ดทดสอบของคุณ ตัวอย่างเช่น คำสั่งเหล่านี้บางส่วนสามารถกำหนดได้ในรูปแบบของกฎที่กำหนดเอง ดังที่แสดงในตัวอย่างโค้ดก่อนหน้านี้ทั้งหมด ตัวอย่างที่ดีของเกณฑ์การยอมรับจะเป็นดังนี้:
- ป๊อปอัปควรอยู่กึ่งกลางหน้าจอในแนวตั้งและแนวนอน
- ควรมีความกว้าง 400 พิกเซล
- ปุ่มควรจัดวางในแนวนอน
- และอื่นๆ
เมื่อคุณอธิบายการออกแบบด้วยประโยคง่ายๆ การแปลงเป็นคำสั่งที่ใช้ซ้ำได้จะง่ายขึ้นและจัดระเบียบโค้ดทดสอบของคุณ
บทสรุป
อย่างที่คุณเห็น แบบฝึกหัดดังกล่าวช่วยให้คุณจัดโครงสร้างการออกแบบและค้นพบรูปแบบเลย์เอาต์ทั่วไปที่สามารถแชร์ผ่านหลาย ๆ องค์ประกอบของเพจได้ แม้ว่าหน้าจะซับซ้อนและประกอบด้วยองค์ประกอบมากมาย คุณก็สามารถหาวิธีจัดกลุ่มในนิพจน์เค้าโครงของคุณได้เสมอ ด้วยวิธีนี้ การทดสอบเลย์เอาต์จะกลายเป็นเครื่องมือสำหรับการพัฒนาที่ขับเคลื่อนด้วยการทดสอบ ช่วยให้คุณออกแบบ ใช้งาน และส่งมอบซอฟต์แวร์แบบเพิ่มหน่วย ซึ่งมีประโยชน์อย่างยิ่งในสภาพแวดล้อมที่คล่องตัว
ทรัพยากร
- Galen Framework (เว็บไซต์อย่างเป็นทางการ)
- Galen Framework, GitHub
- Galen Extras (ห้องสมุด), GitHub
- Galen Bootstrap, GitHub
- “บทแนะนำเกี่ยวกับกรอบงาน Galen” YouTube