Robot framework เรียนให้รู้ ตอน 2
ดูสิว่าร้านขายก๋วยเตี๋ยวใน web application นี้ทำอะไรได้บ้าง แล้วใช้ Robot framework มาจัดการ Test cases ได้อย่างไร
ความเดิมจากตอนที่แล้ว
Requirement 1/10
มาดู requirement ครั้งที่ 1/10 ของเจ้าของร้านกันหน่อย
- อยากให้มีชื่อร้านว่า ก๋วยเตี๋ยวพูนใจ (title name)
- อยากให้มีรายการก๋วยเตี๋ยว (ordered list) อย่างน้อย 3 รายการปรากฏที่หน้าแรก (home page) และมีได้สูงสุดไม่เกิน 5 รายการ
- รายการก๋วยเตี๋ยว (ordered list) ในข้อ 2. จะเพิ่มขึ้นก็ต่อเมื่อลูกค้ากดปุ่ม “สั่ง”
เพื่อนๆต้องทราบก่อนว่าวิธีการที่ผมฝึกฝนนี้จะรับ requirement จากลูกค้าครั้งละ 3 ถึง 5 ข้อสลับกับการนำไปเขียนเป็น web application แล้วส่งมอบให้ลูกค้าพร้อม Robot framework ที่ครอบคลุม (แทบ) ทุกกรณีใน requirement ดังกล่าว
เอาล่ะ เรามาเริ่ม config โปรเจกต์กันต่อเลยนะครับ
Theme & CSS
- ตัว UI จะใช้ Angular Material
- CSS ใช้ Bootstrap 4
Angular Material ทุกอย่างพร้อมสรรพแค่ขยับปลายนิ้ว ทำตามขั้นตอนติดตั้งเลยครับ ง่ายมากๆ
ng add @angular/material
- เลือก Indigo/Pink theme
- HammerJS => Y
- Browser Animations => Y
เสร็จแล้วต่อด้วยติดตั้ง Bootstrap
npm install bootstrap
หลังจากนั้นไฟล์ package.json ต้องมี
config Bootstrap ต่อเลย เปิดไฟล์ angular.json มองหาส่วนที่เขียนว่า
projects > angular-robot > architect > build > options > styles
แล้วยัด path ของ Bootstrap ลงไป ลากมาตั้งแต่ node_modules เลยนะ (อย่าได้ลืมเครื่องหมาย , คั่นระหว่าง property ล่ะ)
"./node_modules/bootstrap/dist/css/bootstrap.css"
หลังการแก้ไขค่า config ทุกครั้งจะต้องหยุด server ก่อน (ถ้าเกิดว่า run อยู่) แล้วรันใหม่อีกครั้ง
หมายเหตุ control + c หยุด server ก่อน จากนั้นรันใหม่ด้วย ng s
เรามาดูว่า Angular Material และ Bootstrap พร้อมใช้งานไหม อาจจะสังเกตจาก tag และ CSS ต่อไปนี้ว่ามันแสดงผลและปรากฏออกมาได้ตามที่ใจนึกไหม
- <mat-toolbar></mat-toolbar>
- คลาส container
เปิดไฟล์ app.module.ts แล้วเพิ่ม MatToolbarModule
import {MatToolbarModule} from '@angular/material/toolbar';...imports: [... MatToolbarModule
],
เปิดไฟล์ app.component.html แล้วแก้ไขให้เป็นดังนี้
เปิดไฟล์ app.component.css แล้วแก้ไขให้เป็นดังนี้
ผลลัพธ์
เอาล่ะมาดูที่ requirement กันครับ
เพิ่มรายละเอียดให้หน้า Home
ต้องการชื่อร้านว่า ก๋วยเตี๋ยวพูนใจ เปิดไฟล์ app.component.ts แก้ไขค่า title
export class AppComponent {title = 'ก๋วยเตี๋ยวพูนใจ';}
เพิ่มปุ่ม “สั่ง” (order) เปิดไฟล์ app.component.html
...<div class="d-flex justify-content-between"> <div class="ordered">item 1</div> <div class="ordered">item 2</div> <div class="ordered">item 3</div></div><div class="mt-2"> <button mat-raised-button color="primary">สั่ง</button></div>...
อย่าลืมเพิ่ม import ส่วน button tag ของ Angular Material เปิดไฟล์ app.module.ts
import {MatToolbarModule} from '@angular/material/toolbar';import {MatButtonModule} from '@angular/material/button';...imports: [... MatToolbarModule, MatButtonModule,],
ผลลัพธ์
เอาล่ะน่าจะพอแล้ว ถัดไปเรามาลุย Robot framework กัน
Basic Selenium2Library
จริงๆต้อง update เพื่อนๆก่อนว่า Selenium2Library นั้นได้เปลี่ยนชื่อเป็น SeleniumLibrary เฉยๆไปแล้วตั้งแต่มันเวอร์ชัน 3.0
เมื่อเราใช้ pip มาจัดการ package ตั้งแต่แรกเริ่ม เราสามารถใช้คำสั่งนี้ดูรายละเอียดได้ครับ
pip freeze
แน่นอนว่าเพื่อดูแลความเข้ากันได้กับเวอร์ชันก่อนหน้านี้ (จะได้ไม่ต้องแก้โค้ด) แม้เราจะเรียกคำสั่ง Selenium2Library ที่เวอร์ชัน 3.0 มาใช้งานก็คือการเรียกคำสั่ง SeleniumLibrary เฉยๆนั่นเอง
จากข้อมูลนี้ลองเปลี่ยนบรรทัด import จากเดิมเรียกใช้ Selenium2Library เป็น SeleniumLibrary แล้วรันครับ ซึ่งโค้ดด้านล่างนี้ผมสั่งเพิ่มให้มันไปเปิด browser Chrome ด้วยเลย
*SettingsLibrary SeleniumLibrary
*Test CasesOpen Firefox Browser Open Browser http://localhost:4200 firefoxOpen Chrome Browser Open Browser http://localhost:4200 chrome
ผล
สรุปได้ว่า Selenium2Library ที่เวอร์ชัน 3.0 นี้เป็นแค่เสื้อคลุมบางๆ (wrapper) แก่ SeleniumLibrary
เอาล่ะมาดูพื้นฐานกัน!
มันประกอบด้วย 4 ส่วนสำคัญ ได้แก่
- Settings
- Test Cases
- Keywords
- Variables
Settings
อธิบายว่าจะใช้ไลบรารีตัวไหน คำสั่งอ้างอิงไลบรารีที่ต้องการคือ Library ตามด้วยชื่อไลบรารีนั้น นอกจากนี้ยังมีคำสั่ง Test Setup และ Test Teardown ไว้เพื่อสั่งว่าก่อนเริ่ม test case และจบ test case จะให้ทำอะไร
เอาเท่านี้ก่อน
Test Cases
อธิบาย test case ทั้งหมดที่ต้องการให้ Robot ทำงานให้ ตัวอย่างเช่น
Open Firefox Browser เป็นชื่อ test case ตั้งอะไรก็ว่าไป
Open Browser อันนี้เป็น key word ซึ่งต้องเขียนให้ถูก แต่มันไม่ถือสาจะใช้พิมพ์เล็กหรือพิมพ์ใหญ่ผสมกันได้หมด
รูปแบบสำหรับ 1 test case คือ
ชื่อ test case + คำสั่งต่างๆ (keywords)
โดยที่คำสั่งต่างๆจะต้องเขียนให้ห่างจาก ชื่อ test case อย่างน้อย 2 ช่องว่าง
เช่น
Open Firefox Browser
Open Browser http://localhost:4200 firefox
หรือ
Open Firefox Browser open browser http://localhost:4200 firefox
โดยทั่วไปแล้วแต่ละ test case จะแยกเป็นอิสระจากัน หมายความว่าถ้า test case ก่อนหน้าพัง test case หลังก็ยังทำงานต่อไป
เอาเท่านี้ก่อน
Keywords
ก็คือคำสั่งต่างๆใน SeleniumLibrary อ้างอิงจากที่นี่
Add Cookie · Add Location Strategy · Alert Should Be Present · Alert Should Not Be Present · Assign Id To Element · Capture Page Screenshot · Checkbox Should Be Selected · Checkbox Should Not Be Selected · Choose Cancel On Next Confirmation · Choose File · Choose Ok On Next Confirmation · Clear Element Text · Click Button · Click Element · Click Element At Coordinates · Click Image · Click Link · Close All Browsers · Close Browser · Close Window · Confirm Action · Create Webdriver · Current Frame Contains · Current Frame Should Contain · Current Frame Should Not Contain · Delete All Cookies · Delete Cookie · Dismiss Alert · Double Click Element · Drag And Drop · Drag And Drop By Offset · Element Should Be Disabled · Element Should Be Enabled · Element Should Be Focused · Element Should Be Visible · Element Should Contain · Element Should Not Be Visible · Element Should Not Contain · Element Text Should Be · Execute Async Javascript · Execute Javascript · Focus · Frame Should Contain · Get Alert Message · Get All Links · Get Cookie · Get Cookie Value · Get Cookies · Get Element Attribute · Get Element Count · Get Element Size · Get Horizontal Position · Get List Items · Get Location · Get Locations · Get Matching Xpath Count · Get Selected List Label · Get Selected List Labels · Get Selected List Value · Get Selected List Values · Get Selenium Implicit Wait · Get Selenium Speed · Get Selenium Timeout · Get Source · Get Table Cell · Get Text · Get Title · Get Value · Get Vertical Position · Get WebElement · Get WebElements · Get Window Handles · Get Window Identifiers · Get Window Names · Get Window Position · Get Window Size · Get Window Titles · Go Back · Go To · Handle Alert · Input Password · Input Text · Input Text Into Alert · Input Text Into Prompt · List Selection Should Be · List Should Have No Selections · List Windows · Location Should Be · Location Should Contain · Locator Should Match X Times · Log Location · Log Source · Log Title · Maximize Browser Window · Mouse Down · Mouse Down On Image · Mouse Down On Link · Mouse Out · Mouse Over · Mouse Up · Open Browser · Open Context Menu · Page Should Contain · Page Should Contain Button · Page Should Contain Checkbox · Page Should Contain Element · Page Should Contain Image · Page Should Contain Link · Page Should Contain List · Page Should Contain Radio Button · Page Should Contain Textfield · Page Should Not Contain · Page Should Not Contain Button · Page Should Not Contain Checkbox · Page Should Not Contain Element · Page Should Not Contain Image · Page Should Not Contain Link · Page Should Not Contain List · Page Should Not Contain Radio Button · Page Should Not Contain Textfield · Press Key · Radio Button Should Be Set To · Radio Button Should Not Be Selected · Register Keyword To Run On Failure · Reload Page · Remove Location Strategy · Select All From List · Select Checkbox · Select Frame · Select From List · Select From List By Index · Select From List By Label · Select From List By Value · Select Radio Button · Select Window · Set Browser Implicit Wait · Set Focus To Element · Set Screenshot Directory · Set Selenium Implicit Wait · Set Selenium Speed · Set Selenium Timeout · Set Window Position · Set Window Size · Simulate · Simulate Event · Submit Form · Switch Browser · Table Cell Should Contain · Table Column Should Contain · Table Footer Should Contain · Table Header Should Contain · Table Row Should Contain · Table Should Contain · Textarea Should Contain · Textarea Value Should Be · Textfield Should Contain · Textfield Value Should Be · Title Should Be · Unselect All From List · Unselect Checkbox · Unselect Frame · Unselect From List · Unselect From List By Index · Unselect From List By Label · Unselect From List By Value · Wait For Condition · Wait Until Element Contains · Wait Until Element Does Not Contain · Wait Until Element Is Enabled · Wait Until Element Is Not Visible · Wait Until Element Is Visible · Wait Until Page Contains · Wait Until Page Contains Element · Wait Until Page Does Not Contain · Wait Until Page Does Not Contain Element · Xpath Should Match X Times
สนใจคำสั่ง (keyword) ไหนก็ไปอ่านรายละเอียดการใช้งานครับ
Variables
ใครที่เคยเขียนโปรแกรมมาก่อนนี่จะง่ายมาก มันก็คือการประกาศตัวแปรเพื่อนำไปใช้งานในส่วนอื่นๆ โดยที่ชื่อของตัวแปรนั้นต้องอยู่ในเครื่องหมาย ${} เช่น
${CREDENTIALS}
ชื่อที่ตั้งนี้จะใช้พิมพ์เล็กหรือใหญ่ก็ได้ ซึ่งรูปแบบการตั้งชื่อตัวแปรนั้นเป็นไปตาม Robot framework variable declaration
เช่น
${NAME} Robot Framework
${VERSION} 2.0
${ROBOT} ${NAME} ${VERSION}
บางคนอาจมีคำถาม ต้องเว้นไว้ 2 ช่องว่างอีกไหม? (ผมขำ) ตอบเลยว่าเว้นไว้เหมือนเดิมจ้า
เอาเท่านี้ก่อน
Time to Robot writing
มาๆเขียน automated test กันได้แล้วล่ะ เปิดไฟล์ hello.robot
Test Setup vs Test Teardown
เริ่มจากขอลองใช้ Test Teardown สั่งให้ปิด browser หลังจากจบการทดสอบ
อ่อ ถ้าเพื่อนๆถามว่าทำไม VS Code ผมไม่มี suggestion เหมือนของพี่ อย่างนั้นให้ติดตั้ง plugin ตัวนี้ครับ
*SettingsLibrary SeleniumLibraryTest Teardown Close Firefox Browser*Test Cases
Open Firefox Browser Open Firefox Browser*KeywordsClose Firefox Browser Close Browser
Test Teardown จะเรียก keyword ที่ชื่อ Close Firefox Browser (ชื่อนี้เราตั้งเอง) หรือถ้าพูดในภาษาโปรแกรมมิ่งธรรมดาคือ คำสั่ง Test Teardown จะเรียกฟังก์ชันชื่อ Close Firefox Browser ให้ทำงาน Close Browser
ถัดไปเราจะทดลองเรียก Test Setup เพื่อให้เปิด browser แก่เรา
*SettingsLibrary SeleniumLibraryTest Setup Open Firefox BrowserTest Teardown Close Firefox Browser*Test Cases
*KeywordsClose Firefox Browser Close BrowserOpen Firefox Browser Open Browser http://localhost:4200 firefox
ขณะนี้เราได้สร้าง keyword ขึ้นมาเอง 2 keywords แล้ว (2 functions) ชื่อ
- Close Firefox Browser
- Open Firefox Browser
ทว่าจะยังรันไม่ผ่าน เพราะ
[ ERROR ] Suite ‘Hello’ contains no tests.
มันแจ้งว่าอย่างน้อยส่วนของ Test Cases จะต้องมีอย่างน้อย 1 test case
Title Name Test Case
ความคาดหวัง: ร้านนี้ควรมีชื่อว่า ก๋วยเตี๋ยวพูนใจ
เพิ่มส่วนของ Test Cases
*Test CasesTitle Name Should Be ก๋วยเตี๋ยวพูนใจ Element Text Should Be //mat-toolbar ก๋วยเตี๋ยวพูนใจ
ทีนี้สมมติว่าวันหนึ่ง ดันมีมือดีมาเปลี่ยนชื่อร้านนี้โดยไม่บอกใคร (เขาทำไปเพื่ออะไรนะ?) จากร้าน ก๋วยเตี๋ยวพูนใจ เปลี่ยนเป็น ยายน้อยโพทะยาน ดังนี้
export class AppComponent { title = 'ยายน้อยโพทะยาน';}
แล้วปรากฏว่า Robot มาทำงาน เราก็จะได้รู้ว่าสิ่งแปลกได้เกิดขึ้นแล้ว
Ordered List
เขาว่าอย่างน้อยต้องมี 3 รายการก๋วยเตี๋ยวที่ได้สั่งไปแล้วปรากฏให้เห็นเสมอ แต่ก็ไม่ให้เกิน 5 รายการ
*Test CasesTitle Name Should Be ก๋วยเตี๋ยวพูนใจ Element Text Should Be //mat-toolbar ก๋วยเตี๋ยวพูนใจOrdered List Should Show Greater Than Or Equal To 3 And Less Than Or Equal To 5 ${count}= Get Element Count css:div.ordered Should Be True ${count} >= 3 Should Be True ${count} <= 5
สมมติว่า logic ของโปรแกรมเมอร์ผิดพลาด ดันไปเพิ่มของในรายการ show case นี้เป็น 6 ชิ้น ดังนี้
<div class="d-flex justify-content-between"> <div class="ordered">item 1</div> <div class="ordered">item 2</div> <div class="ordered">item 3</div> <div class="ordered">item 4</div> <div class="ordered">item 5</div> <div class="ordered">item 6</div></div>
แล้วปรากฏว่า Robot มาทำงาน
หรือไปลดของในรายการ show case นี้ต่ำกว่า 3 ชิ้น ดังนี้
<div class="d-flex justify-content-between"> <div class="ordered">item 1</div> <div class="ordered">item 2</div></div>
แล้วปรากฏว่า Robot มาทำงาน
Clicked Order Button
กดปุ่ม สั่ง แล้วรายการก๋วยเตี๋ยวจะต้องเพิ่มขึ้น
อันนี้มันก็แปลกๆนะ แต่ใช่ว่าทำไม่ได้ ความคิดคือ ก่อนกดปุ่มให้นับจำนวนรายการไว้ก่อน และหลังกดปุ่มจึงนับจำนวนรายการอีกครั้ง มากกว่า 1 จึงถือว่าถูกต้อง
เรามาเขียนโค้ดเพิ่มเพื่อให้รายการก๋วยเตี๋ยวนี้ dynamic ก่อนนะครับ
- เปลี่ยน static เป็น dynamic ด้วย *ngFor
- กำหนด ordered list มีชนิดข้อมูลเป็น array
ไฟล์ app.component.html
<div class="d-flex justify-content-between" *ngFor="let order of orderedList"> <div class="ordered">{{order.name}}</div></div>
ไฟล์ app.component.ts
export class AppComponent { title = 'ก๋วยเตี๋ยวพูนใจ'; orderedList = [ { name: 'item1' }, { name: 'item2' }, { name: 'item3' } ];}
แล้วให้ Robot รัน cases เดิมให้ผ่านก่อน
แก้ไขต่อได้
ไฟล์ app.component.html เพิ่ม click event ให้กับปุ่มสั่ง ตั้งชื่อว่า order
<div class="mt-2"> <button mat-raised-button color="primary" (click)="order()">สั่ง</button></div>
ไฟล์ app.component.ts เพิ่มเมธอด order และ logic
order() { const newOrder = { name: 'item4' }; this.orderedList.push(newOrder);}
มาเขียน test case นี้ในส่วนของ Robot กัน
Ordered List Should Increase 1 After Click Order Button ${beforCount}= Get Element Count css:div.ordered Click Button //button ${afterCount}= Get Element Count css:div.ordered Should Be True ${afterCount} - ${beforCount} == 1
แต่ถ้าสมมติว่า logic ไม่ดี กดปุ่มสั่งแล้วเบิ้ลคู่ เช่น
order() { const newOrder = { name: 'item4' }; this.orderedList.push(newOrder); this.orderedList.push(newOrder); // double!}
ผล
เรียบร้อยเป็นว่าสำเร็จแล้วสำหรับ requirement 1/10
Locating elements
เพื่อนๆจะสังเกตเห็นว่ามีโค้ดอยู่หลายส่วนที่เราไม่เข้าใจ ต้องอาศัยการอ่านทำความเข้าใจหรือแสวงหาตัวอย่างอื่นๆในอินเตอร์เน็ตประกอบ เช่นคำสั่งนี้
Click Button //button
เพื่อนๆมือใหม่อาจเดาได้ว่า สั่งให้มันกดปุ่ม ใช่แล้วครับ เราสั่งให้มันกดปุ่ม แล้วรู้ไหมสัญลักษณ์ //button นี้หมายถึงอะไร เป็นต้น
พารามิเตอร์ส่วนใหญ่ของ SeleniumLibrary เขาต้องการ locator หมายถึงที่ที่ element ตัวนี้แสดงตนอยู่ในไฟล์ .html ไม่ว่าจะเป็นรูปแบบของ id, name, tag, css หรืออื่นๆ เพื่อนๆสามารถอ่านรายละเอียดได้ที่หัวข้อ Locating elements ของ SeleniumLibrary document ครับ
โค้ดทั้งหมด
เอาล่ะลองทำดูเราทำได้
อ่านต่อตอนที่ 3
อ้างอิง
https://www.w3schools.com/xml/xpath_syntax.asp
https://www.guru99.com/xpath-selenium.html