Thực hành tốt nhất cho kiểm thử tự động Selenium - nhà cái 888b

| Mar 21, 2025 min read

10 tháng 5 năm 2023 | Công nghệ máy tính

Trong hai bài viết trước “Sử dụng cơ bản của Selenium WebDriver” và “Sử dụng các tính năng nâng cao của Selenium WebDriver”, chúng ta đã tìm hiểu về cách sử dụng các chức năng cơ bản và nâng cao của Selenium WebDriver. Hai bài viết này tập trung nhiều hơn vào việc luyện tập API của Selenium WebDriver từ góc độ chi tiết thực thi.

Bài viết này sẽ thảo luận về “Thực hành tốt nhất để xây dựng một dự án kiểm thử tự động Selenium là gì?” Phần này sẽ xem xét nhiều hơn từ góc độ thiết kế và kiến trúc cấp cao, suy nghĩ từ trên xuống dưới về cách xây dựng một dự án kiểm thử lớn. Bao gồm: Cần chuẩn bị 789 crore club apk gì trước khi mã hóa? Có một nguyên tắc hướng dẫn cơ bản nào không? Làm thế nào để sắp xếp mã kiểm thử? Làm thế nào để chọn trình định vị phù hợp tùy theo tình huống? Chúng ta sẽ bàn cổng game trực tuyến r88 luận từng vấn đề một.

Tất cả các ví dụ mã trong bài viết này đều được mô tả bằng ngôn ngữ Python. Dưới đây là phiên bản Python, Selenium và Chrome mà bài viết sử dụng:

  • Phiên bản Python: 3.11.3
  • Phiên bản Selenium: 4.9.1
  • Phiên bản trình duyệt Chrome: 113

1. Chuẩn bị trước khi mã hóa và nguyên tắc hướng dẫn cơ bản

Kiểm thử một trang web giống như phát triển một dự án cho các trường hợp kiểm thử của trang web đó. Vì vậy, các ý tưởng và tư duy trong phát triển dự án có thể áp dụng ở đây. Khi nhận yêu cầu kiểm thử, đừng ngay lập tức sa đà vào các chi tiết kỹ thuật như làm thế nào để thao tác với nút bấm, trường nhập liệu hoặc hộp thả xuống. Thay vào đó, hãy đứng từ góc độ người dùng cuối cùng để phân tích logic tương tác và mối quan hệ phụ thuộc của yêu cầu kiểm thử, sau đó chia nhỏ nó thành các trường hợp kiểm thử độc lập. Đối với mỗi trường hợp kiểm thử, không phải bước nào cũng cần phải tự động hóa hoàn toàn bằng Selenium. Hãy tự động hóa phần quan trọng và sử dụng cấy ghép dữ liệu hoặc gọi API cho các phần không quan trọng khác.

Sau khi phân tích và chia nhỏ, chúng ta nên sắp xếp mã kiểm thử ra sao? Chúng ta sẽ thảo luận vấn đề này dưới đây.

2. Làm thế nào để sắp xếp mã kiểm thử?

Làm thế nào để tổ chức và sắp xếp mã kiểm thử? Tức là chiến lược nào nên được áp dụng để mã trở nên gọn gàng và dễ bảo trì hơn.

Chúng ta vẫn sử dụng ví dụ thực tế để minh họa vấn đề cần thảo luận. Dưới đây là hình ảnh GIF về quá trình kiểm thử tự động trang biểu mẫu Web bằng Selenium: !Biểu mẫu Web Selenium ví dụ trang Như bạn có thể thấy, GIF này minh họa mã kiểm thử tự động điền văn bản, mật khẩu, chọn giá trị từ hộp thả xuống và nhập ngày tháng, sau đó nhấn nút gửi và chuyển đến trang đã gửi.

Mã kiểm thử Python gốc (original_form_test.py) tương ứng với GIF trên như sau:

from unittest import TestCase
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class TestForm(TestCase):
  def setUp(self) -> None:
    self.driver = webdriver.Chrome()
    self.addCleanup(self.driver.quit)
  def test_web_form(self) -> None:
    # Mở trang biểu mẫu
    self.driver.get('
    self.assertEqual(self.driver.title, 'Biểu mẫu Web')
    # Nhập văn bản
    text_input_elem = self.driver.find_element(By.ID, 'my-text-id')
    text_input_elem.send_keys('Selenium')
    # Nhập mật khẩu
    password_elem = self.driver.find_element(By.NAME, 'my-password')
    password_elem.send_keys('Selenium')
    # Chọn từ hộp thả xuống
    dropdown_elem = Select(self.driver.find_element(By.NAME, 'my-select'))
    dropdown_elem.select_by_value('2')
    # Nhập ngày tháng
    date_input_elem = self.driver.find_element(By.XPATH, '//input[@name="my-date"]')
    date_input_elem.send_keys('10/05/2023')
    # Nhấn nút Gửi
    submit_button_elem = self.driver.find_element(By.XPATH, '//button[@type="submit"]')
    submit_button_elem.click()
    # Chờ đợi chuyển sang trang đã gửi
    WebDriverWait(self.driver, 10).until(EC.title_is('Biểu mẫu Web - trang đích'))
    # Kiểm tra kết quả
    message = self.driver.find_element(By.ID, 'message').text
    self.assertEqual(message, 'Đã nhận!')

Như bạn có thể thấy, đây là cách viết mã kiểm thử phổ biến và trực tiếp nhất. Tuy nhiên, cách viết này tồn tại một số vấn đề:

  • Mã kiểm thử (assertEqual) và mã định vị và thao tác phần tử (find_element ... send_keys ... click) bị gắn kết chặt chẽ với nhau. Do đó, nếu các dấu hiệu định vị phần tử thay đổi hoặc cách thức thao tác phần tử thay đổi, đoạn mã kiểm thử này cần được sửa đổi.
  • Nếu muốn viết mã kiểm thử khác liên quan đến trang này hoặc các trang phụ thuộc vào trang này, mã định vị và thao tác phần tử sẽ phải được viết lại từ đầu.

Để giải quyết các vấn đề trên, cần cải tiến cấu trúc mã và cách sắp xếp mã, cụ thể là áp dụng một mẫu thiết kế — Mẫu Trang Đối Tượng. Mẫu Trang Đối Tượng (Page Object Model) mượn tư tưởng từ lập trình hướng đối tượng, là một mẫu thiết kế được sử dụng rộng rãi trong kiểm thử tự động nhằm giảm thiểu mã lặp lại và tăng cường khả năng bảo trì. Một trang đối tượng là một lớp hướng đối tượng, lưu trữ các phần tử Web của cùng một trang trong một đối tượng; khi cần tương tác với giao diện người dùng của đối tượng đó, không trực tiếp truy cập các phần tử Web của trang mà thông qua việc gọi các phương thức mà đối tượng cung cấp. Lợi ích của việc này là nếu giao diện của một trang thay đổi, mã kiểm thử không cần thay đổi mà chỉ cần chỉnh sửa mã bên trong đối tượng trang tương ứng.

Tóm lại, lợi ích của việc sử dụng Mẫu Trang Đối Tượng bao gồm:

  • Phân tách mã kiểm thử khỏi mã đặc thù của trang (tăng tính gọn gàng);
  • Các phần tử và chức năng của trang được đóng gói trong thuộc tính và phương thức của đối tượng trang, thay vì rải rác khắp mã kiểm thử (giảm mã lặp lại và tăng khả năng bảo trì).

Dưới đây là cách thiết kế cấu trúc dự án kiểm thử bằng Mẫu Trang Đối Tượng:

$ tree
.
├─ pages
│  ├─ form.py
│  └─ form_target.py
└─ optimized_form_test.py

Có thể thấy, các đối tượng trang dành riêng cho từng trang được đặt trong thư mục pages, và khi viết các trường hợp kiểm thử, chỉ cần gọi các phương thức tương ứng.

Dưới đây là mã đã được tối ưu hóa. Mã đối tượng trang Form (form.py):

from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from pages.form_target import FormTarget
from typing import Self
class Form:
  def __init__(self, driver) -> None:
    self.driver = driver
  def open(self) -> Self:
    self.driver.get('
    return self
  def get_title(self) -> str:
    return self.driver.title
  def input_text(self, text: str) -> Self:
    elem = self.driver.find_element(By.ID, 'my-text-id')
    elem.send_keys(text)
    return self
  def input_password(self, password: str) -> Self:
    elem = self.driver.find_element(By.NAME, 'my-password')
    elem.send_keys(password)
    return self
  def select_from_dropdown(self, value: str) -> Self:
    elem = Select(self.driver.find_element(By.NAME, 'my-select'))
    elem.select_by_value(value)
    return self
  def input_date(self, date: str) -> Self:
    elem = self.driver.find_element(By.XPATH, '//input[@name="my-date"]')
    elem.send_keys(date)
    return self
  def submit(self) -> FormTarget:
    elem = self.driver.find_element(By.XPATH, '//button[@type="submit"]')
    elem.click()
    # Chờ đợi chuyển sang trang đã gửi
    WebDriverWait(self.driver, 10).until(EC.title_is('Biểu mẫu Web - trang đích'))
    # Trả về đối tượng FormTarget
    return FormTarget(self.driver)

Mã đối tượng trang FormTarget (form_target.py):

from selenium.webdriver.common.by import By
class FormTarget:
  def __init__(self, driver) -> None:
    self.driver = driver
  def get_message_text(self) -> str:
    return self.driver.find_element(By.ID, 'message').text

Mã kiểm thử sử dụng hai đối tượng trang trên (optimized_form_test.py):

from unittest import TestCase
from selenium import webdriver
from pages.form import Form
class TestForm(TestCase):
  def setUp(self) -> None:
    self.driver = webdriver.Chrome()
    self.addCleanup(self.driver.quit)
  def test_web_form(self) -> None:
    # Mở trang biểu mẫu
    form_page = Form(self.driver)
    form_page.open()
    self.assertEqual(form_page.get_title(), 'Biểu mẫu Web')
    # Nhập liệu
    form_target_page = form_page.input_text('Selenium') \
      .input_password('Selenium') \
      .select_from_dropdown('2') \
      .input_date('10/05/2023') \
      .submit()
    # Kiểm tra kết quả
    message = form_target_page.get_message_text()
    self.assertEqual(message, 'Đã nhận!')

Như bạn có thể thấy, mã đã được tối ưu hóa rõ ràng hơn nhiều. Dưới đây là một số lưu ý khi sử dụng Mẫu Trang Đối Tượng:

  • Kiểm tra là một phần của logic kiểm thử, nên được giữ trong mã kiểm thử, do đó, không nên có mã kiểm tra hoặc xác minh trong đối tượng trang;
  • Đối tượng trang chỉ nên phơi bày các dịch vụ của trang thông qua các phương thức công khai, các chi tiết nội bộ khác không nên phơi bày ra ngoài.

Tiếp theo, chúng ta sẽ chú ý đến một số chi tiết thực hiện, việc sử dụng trình định vị là một phần công việc thường xuyên khi viết mã kiểm thử Selenium. Dưới đây là các thực hành tốt nhất liên quan đến việc sử dụng trình định vị.

3. Làm thế nào để chọn trình định vị phù hợp tùy theo tình huống?

Trong bài viết trước “Sử dụng cơ bản của Selenium WebDriver”, chúng ta đã giới thiệu 8 phương pháp cơ bản để định vị phần tử trong Selenium. Vậy khi nào nên sử dụng loại trình định vị nào? Dưới đây là các thực hành tốt nhất.

Trình định vị id nên là lựa chọn đầu tiên, chính xác và nhanh chóng; nếu phần tử không có id, thì sử dụng css selector; nếu hai phương pháp này không khả dụng, mới chọn xpath (hiệu suất kém hơn so với hai phương pháp trước); thông thường trên một trang sẽ có nhiều phần tử cùng tag, vì vậy trình định vị tag thường được sử dụng để chọn một nhóm phần tử.

Tóm lại, bài viết này đã giới thiệu nguyên tắc hướng dẫn cơ bản để phân tích yêu cầu khi xây dựng một dự án kiểm thử lớn, chiến lược thực hành để sắp xếp mã kiểm thử và thứ tự khuyến nghị khi sử dụng trình định vị. Tất cả mã nguồn trong bài viết này đã được tải lên GitHub cá nhân của tôi, mời bạn theo dõi. Hy vọng sau khi đọc bài viết này, chúng ta sẽ có một cái nhìn tổng thể về kiểm thử tự động Selenium từ phân tích yêu cầu, thực hiện mã đến các chi tiết thực hiện.

[1] Thực hành kiểm thử | Selenium - www.selenium.dev #Selenium #Kiểm thử tự động #Thiết kế kiến trúc #Python