Kỷ Nguyên Robot: Locators Của Ứng Dụng Web
Đây là bài tiếp theo trong series Kỷ Nguyên Robot: Tự động hóa hay lụi tàn để hướng dẫn các bạn cách lựa chọn và xây dựng chuỗi locator hợp lý và ít phải thay đổi khi giao diện của ứng dụng thay đổi trong quá trình phát triển. Ở đây, giả định rằng các bạn đã có kiến thức cơ sở về HTML.
1: Tổng quan về Locator
Như các bạn đã biết, Selenium hay các framework sử dụng chung WebDriver Protocol như Protractor, Appium sử dụng các chuỗi để định vị một hay nhiều đối tượng tương tự nhau (element) của ứng dụng web trên trình duyệt. Các chuỗi định vị này được gọi chung bằng cái tên Locator. Có tất cả 8 loại locator như sau:
- Id: Dựa trên thuộc tính id của thẻ HTML mô tả đối tượng.
- Name: Dựa trên thuộc tính name của thẻ HTML mô tả đối tượng.
- Class name: Dựa trên thuộc tính class của thẻ HTML mô tả đối tượng.
- Tag name: Dựa trên tên của thẻ HTML mô tả đối tượng.
- Link text: Dựa trên nội dung hiển thị của một hyperlink (thẻ <a>)
- Partial link text: Dựa trên một phần nội dung hiển thị của một hyperlink
- Xpath: Xpath là một ngôn ngữ truy vấn dữ liệu dạng cây. Về nguyên tắc có thể sử dụng xpath để lựa chọn node của bất kỳ dữ liệu dạng cây nào, bao gồm cả HTML hay XML. Xpath có thể trả về 1 node hay 1 tập hợp node tuỳ thuộc vào cú pháp cụ thể của truy vấn.
- CSS Selector: CSS Selector là ngôn ngữ sử dụng các thuộc tính CSS của một thẻ HTML để lựa chọn. Đây là locator phù hợp nhất khi sử dụng chung với trình duyệt Internet Explorer.
2: Cách lựa chọn Locator phù hợp
Để lựa chọn đúng locator phù hợp ta cần căn cứ vào đặc điểm của các loại locator và nhu cầu sử dụng locator, trước tiên cần xác định là ta cần lấy 1 đối tượng duy nhất hay một tập hợp các đối tượng tương tự để lựa chọn locator phù hợp. Ví dụ:
- Id: luôn luôn là duy nhất, không có hai đối tượng được phép có cùng id trên cùng 1 page. Do đó về cơ bản Id chỉ để phục vụ mục đích tìm đối tượng duy nhất. Đây đồng thời cũng là locator nhanh nhất và đáng tin cậy nhất để lấy một đối tượng duy nhất. Tuy nhiên cần cẩn thận và hạn chế sử dụng với các đối tượng có id độngnhư các check box trong một bảng. Với các đối tượng có id động nên sử dụng xpath. Do id không phải là thuộc tính bắt buộc phải có, nên không phải lúc nào cũng sử dụng được locator này.
- Name: Nhiều đối tượng có thể có cùng thuộc tính name, do đó nếu sử dụng để lấy đối tượng duy nhất thì phải kiểm tra cẩn thận trên DOM để chắc chắn rằng nó là duy nhất. Đây cũng không phải là thuộc tính bắt buộc của thẻ HTML.
- Class name: Thường sẽ có nhiều đối tượng có cùng class name, và một đối tượng có thể thuộc nhiều class khác nhau, do đó locator này chỉ sử dụng để lấy một nhóm các đối tượng có cùng 1 class nào đó. Đây cũng không phải là thuộc tính bắt buộc của thẻ HTML.
- Tag Name: Sẽ có rất nhiều đối tượng có cùng class name trên 1 page, do đó về cơ bản là tag name chỉ sử dụng để lấy tất cả đối tượng có cùng tag name. Đây là thuộc tính bắt buộc phải có của thẻ HTML.
- Link text và partial link text: Trả về một hay nhiều đối tượng phụ thuộc vào số lượng hyperlink có cùng nội dung hoặc 1 phần nội dung. Thẻ a của hyperlink có thể không có nội dung text mà thay vào đó là hình ảnh, nên cũng không áp dụng được cho tất cả hyperlink.
- Xpath: Locator dạng này là linh hoạt nhất, có thể được tạo ra từ vị trí tuyệt đối của đối tượng trên tài liệu DOM, hoặc dựa trên bất kỳ thuộc tính nào khác của các thẻ HTML, thậm chí là thẻ HTML có liên quan.
- CSS Selector: Locator dạng này có thể tận dụng một số thuộc tính của thẻ HTML tương tự xpath, có khả năng xác định đối tượng nhất định tốt hơn xpath nhưng khả năng truy vấn từ đối tượng liên quan thì yếu hơn xpath (Ví dụ khi cần truy vấn 1 checkbox nằm cùng hàng trên 1 bảng với một ô chứa văn bản nào đó). Đây là locator hoạt động ổn định nhất trên Internet Explorer.
3: Cách lấy locator trong từng trường hợp
Ví dụ tôi có các thẻ HTML như sau trên page cần test:
1. Thẻ input
<input type="text" class="form-control" placeholder="Please enter your Message" name="message" id="user-message">
2. Hyperlink
<a href="./basic-first-form-demo.html">Simple Form Demo</a>
3. Bảng
<table id="example" class="display dataTable no-footer" width="100%" cellspacing="0" role="grid" aria-describedby="example_info" style="width: 100%;">
<tbody>
<tr role="row" class="odd">
<td data-search="Airi Satou" class="sorting_1">A. Satou</td>
<td>Accountant</td>
<td>Tokyo</td>
<td>33</td>
<td data-order="1227830400">Fri 28th Nov 08</td>
<td data-order="162700">$162,700/y</td>
</tr><tr role="row" class="even">
<td data-search="Bradley Greer" class="sorting_1">B. Greer</td>
<td>Software Engineer</td>
<td>London</td>
<td>41</td>
<td data-order="1350082800">Sat 13th Oct 12</td>
<td data-order="132000">$132,000/y</td>
</tr><tr role="row" class="odd">
<td data-search="Brenden Wagner" class="sorting_1">B. Wagner</td>
<td>Software Engineer</td>
<td>San Francisco</td>
<td>28</td>
<td data-order="1307401200">Tue 7th Jun 11</td>
<td data-order="206850">$206,850/y</td>
</tr>
</tbody>
</table>
Đối với các locator không duy nhất (có thể trả về nhiều đối tượng) thì hàm findElement trong Selenium sẽ trả về đối tượng đầu tiên trong DOM, để lấy toàn bộ cần sử dụng hàm findElements (số nhiều).
Id: Để lấy text box đầu tiên bằng id tôi sẽ sử dụng cú pháp sau trong Selenium
driver.findElement(By.id("user-message"));
Name:
driver.findElement(By.name("message"));
Class Name:
driver.findElement(By.className("form-control"));
Tag Name:
driver.findElement(By.tagName("input"));
Link Text và Partial Link Text đối với ví dụ 2 bên trên
driver.findElement(By.linkText("Simple Form Demo"));
driver.findElement(By.partialLinkText("Form Demo"));
Xpath đối với text box số 1: Xpath này là xpath tương đối được tính ra từ thuộc tính id
driver.findElement(By.xpath("//input[@id='user-message']"));
CSS Selector đối với text box số 1:
driver.findElement(By.cssSelector("input[name=message]"));
4: Cách tính toán XPATH
Để lấy XPATH tuyệt đối (theo đường dẫn trong DOM) trên trình duyệt thì rất đơn giản. Tuy nhiên do không khuyến khích sử dụng loại XPATH này nên tôi sẽ không hướng dẫn cụ thể cách lấy. Lý do là vì XPATH kiểu này luôn thay đổi mỗi khi layout của trang web có thay đổi.
Nếu có thể sử dụng locator khác thì không nên sử dụng XPATH, vì đây là locator chậm nhất.
Không sử dụng xpath theo 1 thuộc tính đơn lẻ của thẻ html (ví dụ id hoặc name), trường hợp này chỉ cần lấy thẳng thuộc tính đó.
Dưới đây là một số ví dụ thường gặp khi bắt buộc phải sử dụng XPATH
1. Các thuộc tính của đối tượng không duy nhất nhưng tổ hợp của 2 hay nhiều thuộc tính là duy nhất
driver.findElement(By.xpath("//input[@name='message' AND @class='form-control']"));
2. Lựa chọn một đối tượng theo toàn bộ hoặc một phần text hiển thị của nó
Toàn bộ: //td[text()='Software Engineer']
Một phần: //td[contains(text(),'Engineer')]
3. Lựa chọn một đối tượng theo anh em của nó, ví dụ tôi cần lấy tên của tất cả
những người là "Software Engineer" trong ví dụ về bảng số 3 đầu mục 3 ở bên trên,
trường hợp này ô tên và ô nghề nghiệp là anh em cùng cha (dòng):
//table[@id='example']/tbody/tr[td/text()='Software Engineer']/td[1]
Ví dụ này có thể diễn giải như sau: Duyệt bảng có id là example, đến thẻ tbody (thân bảng),
tìm 1 thẻ tr (dòng) mà trong đó có 1 td (ô) có text là 'Software Engineer',
sau đó lấy thẻ td (ô) đầu tiên của thẻ tr (dòng) tìm được (Tên người cần tìm).
5: Cách sử dụng và tính CSS Selector
CSS Selector là locator ổn định nhất trên IE, đồng thời gần như là locator duy nhất áp dụng được với ứng dụng sử dụng Polymer framework và ReactJS. Do đó khi rơi và các trường hợp trên thì nên sử dụng CSS Selector.
Lấy CSS Selector theo một thuộc tính
Ví dụ ta có một thẻ HTMl như sau
<input type="text" id="fistname" name="first_name" class="myForm">
Ta có thể lấy đối tượng trên theo những cách sau:
1. Theo cách tổng quát
"input[name='first_name']"
2. Theo id:
"input#firstname"
3. Theo class
"input.myForm"
Lấy CSS Selector theo nhiều thuộc tính
Ví dụ với thẻ:
<div class="ajax_enabled" style="display:block">
Ta có thể select bằng string sau:
"div[class='ajax_enabled'] [style='display:block']"
Theo một thuộc tính KHÔNG chứa giá trị nhất định
Ví dụ với các thẻ sau:
<div class="day past calendar-day-2017-02-13 calendar-dow-1 unavailable">
<div class="day today calendar-day-2017-02-14 calendar-dow-2 unavailable">
<div class="day calendar-day-2017-02-15 calendar-dow-3">
<div class="day calendar-day-2017-02-16 calendar-dow-4">
Tôi muốn chọn đối tượng không chứa thuộc tính unavailable:
"div[class*=calendar-day-]:not([class*='unavailable'])"
Lấy đối tượng con theo đối tượng cha
Ví dụ với thẻ sau:
<div id="logo">
<img src="./logo.png" />
</div>
Để lấy đối tượng img ta có thể sử dụng chuỗi sau:
"div#logo img"
Lấy các đối tượng con của một đối tượng cha
Ví dụ với các thẻ sau
<ul id="fruit">
<li>Apple</li>
<li>Orange</li>
<li>Banana</li>
</ul>
Có thể lấy đối tượng Orange bên trên theo chuỗi sau:
"ul#fruit li:nth-of-type(2)"
Hoặc lấy đối tượng cuối cùng:
"ul#fruit li:last-child"
Lấy trong trường hợp Id động (Vd các check box ở trong 1 bảng thường có id động do chúng được sinh ra theo data)
Ví dụ với các thẻ sau:
<div id="123_randomId">
<div id="randomId_456">
<div id="123_pattern_randomId">
1. Lấy với thuộc tính bắt đầu bằng chuỗi cố định:
"div[id^='123']" -- Ở đây ^= nghĩa là bắt đầu bằng
2. Lấy với thuộc tính kết thúc bằng chuỗi cố định:
"div[id$='456']" -- Ở đây $= nghĩa là kết thúc bằng
3. Lấy với thuộc tính có chứa chuỗi con cố định bằng một trong 2 cách sau:
"div[id*='_pattern_']" -- Dấu * nghĩa là chuỗi con bất kỳ
"div:contains('_pattern_')" -- Từ khoá contains có nghĩa là có chứa
5: Kết
Như vậy là chúng ta đã đi qua tất cả các loại locator được sử dụng để định vị đối tượng trong Selenium cũng như các framework sử dụng WebDriver Protocol. Để có thể lựa chọn được loại locator phù hợp nhất các bạn cần nắm thật rõ đặc điểm của từng loại locator cũng như giới hạn của trình duyệt hay framework. Chúc các bạn có được những kiến thức bổ ích cho sự nghiệp của mình.