NỘI DUNG BÀI HỌC
✅ Phân biệt rõ ràng hai khái niệm: Class và Object.
✅ Nắm vững cách khởi tạo đối tượng với
__init__
và vai trò của từ khóa self
.✅ Xây dựng các Class có đầy đủ Attributes và Methods.
✅ Hiểu rõ về Scope để tránh lỗi logic.
✅ Vận dụng OOP để xây dựng Page Object Model (POM) đầu tiên với Python Playwright.
1. Tại sao phải dùng OOP? Code "lộn xộn" vs. Code "ngăn nắp"
Hãy tưởng tượng bạn đang xây dựng một bộ kịch bản kiểm thử cho trang web thương mại điện tử. Bạn có các test case cho việc: Đăng nhập, Tìm kiếm sản phẩm, Thêm vào giỏ hàng, Thanh toán.
Kịch bản "lộn xộn" (Procedural Script): Mỗi file test case (test_login.py
, test_search.py
) đều chứa các dòng code lặp đi lặp lại để tìm đến ô username, ô password, nút login. Khi giao diện trang đăng nhập thay đổi (ví dụ: ID của ô username đổi từ "user" thành "username"), bạn phải vào TẤT CẢ các file và sửa lại. Rất tốn thời gian và dễ gây sai sót.
Kịch bản "ngăn nắp" (Object-Oriented Script): Bạn tạo một "bản thiết kế" duy nhất cho trang đăng nhập, gọi là class LoginPage
. Bên trong đó, bạn định nghĩa một lần duy nhất các element (username_input
, password_input
, login_button
) và các hành động (login(user, pass)
).
Khi giao diện thay đổi, bạn chỉ cần vào file LoginPage.py
và sửa MỘT LẦN DUY NHẤT. Tất cả các test case khác gọi đến LoginPage
sẽ tự động được cập nhật.
👉 Lập trình Hướng đối tượng (OOP) chính là công cụ giúp chúng ta biến code từ "lộn xộn", khó bảo trì thành "ngăn nắp", có tổ chức và dễ dàng tái sử dụng.
2. Khái niệm Cốt lõi - Lớp (Class) và Đối tượng (Object)
Đây là hai khái niệm cơ bản nhất của OOP.
-
Class (Lớp): Là một bản thiết kế 📐 hoặc một khuôn mẫu. Nó định nghĩa các đặc điểm (thuộc tính) và hành vi (phương thức) chung cho một loại đối tượng nào đó.
-
Ví dụ cuộc sống: Bản thiết kế của một chiếc xe hơi, định nghĩa rằng xe sẽ có bánh xe, cửa sổ, màu sơn và có thể chạy, dừng, rẽ trái, rẽ phải.
-
-
Object (Đối tượng) - Ngôi nhà thực tế 🏠: Là một thể hiện cụ thể được tạo ra từ bản thiết kế (Class). Mỗi đối tượng sẽ có những giá trị đặc điểm riêng của nó.
-
- Ví dụ cuộc sống: Chiếc Vinfast Fadil màu đỏ của bạn, chiếc Mercedes C300 màu đen của sếp. Cả hai đều là những đối tượng được tạo ra từ bản thiết kế "Xe hơi", nhưng có các giá trị thuộc tính (hãng, model, màu sắc) khác nhau.
Ví dụ về Class và Object:
# Class: Bản thiết kế
class Dog:
# Phương thức (hành động)
def bark(self):
print("Gâu gâu!")
# Object: Tạo ra một chú chó cụ thể từ bản thiết kế
my_dog = Dog()
# Gọi hành động của đối tượng đó
my_dog.bark()
# Output: Gâu gâu!
3. "Trái tim" của Class: __init__
và self
3.1 __init__
- Phương thức khởi tạo
__init__
là một phương thức đặc biệt (gọi là constructor) sẽ tự động được gọi ngay khi bạn tạo ra một đối tượng mới từ class. Nhiệm vụ chính của nó là gán các giá trị ban đầu cho các thuộc tính của đối tượng.
🔹 Ví dụ trong cuộc sống: Khi một ngôi nhà được xây xong (đối tượng được tạo), đội thi công (hàm __init__
) sẽ ngay lập tức sơn tường màu trắng, lắp cửa sổ loại X (gán các giá trị thuộc tính ban đầu).
3.2 self
- Đại diện cho chính đối tượng (object)
self
là một quy ước trong Python, dùng để chỉ chính đối tượng đang được đề cập đến. Nó giúp phân biệt các thuộc tính của đối tượng (self.name
) với các biến thông thường khác (name
). self
luôn là tham số đầu tiên của mọi phương thức trong một class.
🔹 Ví dụ trong cuộc sống: Khi bạn ở trong nhà của mình và nói "phòng khách của tôi", từ "tôi" ở đây chính là self
, nó chỉ rõ là phòng khách của ngôi nhà cụ thể này chứ không phải của nhà hàng xóm.
class User:
# Hàm khởi tạo, được gọi khi new_user = User(...)
def __init__(self, user_name, user_email):
print(f"Một user mới đang được tạo...")
# Dùng `self` để gán giá trị cho các thuộc tính của chính đối tượng này
self.name = user_name
self.email = user_email
self.is_active = True # Gán một giá trị mặc định
# Tạo ra 2 đối tượng User khác nhau
user_A = User("Alice", "alice@email.com")
user_B = User("Bob", "bob@email.com")
# Mỗi đối tượng có thuộc tính `name` của riêng nó
print(f"Tên user A: {user_A.name}") # Output: Tên user A: Alice
print(f"Tên user B: {user_B.name}") # Output: Tên user B: Bob
🔹 Ứng dụng trong Automation: Khi khởi tạo một Page Object, chúng ta thường truyền đối tượng page vào __init__
để trang đó có thể tương tác với trình duyệt.
class SearchPage:
def __init__(self, page):
self.page = page
self.search_input = "#search"
self.search_button = "#searchBtn"
def search(self, keyword):
self.page.fill(self.search_input, keyword)
self.page.click(self.search_button)
✍️ Bài tập thực hành phần 3
Thiết kế Xe hơi: Viết một class Car
với hàm __init__
nhận vào 2 tham số là brand
(hãng xe) và model
. Bên trong __init__
, hãy gán chúng cho các thuộc tính self.brand
và self.model
. Sau đó, tạo ra 2 đối tượng xe hơi khác nhau và in ra thông tin của chúng.
4. Thuộc tính và Phương thức - "Đặc điểm" và "Hành động"
-
Attributes (Thuộc tính): Là các biến gắn liền với một đối tượng, dùng để lưu trữ trạng thái hoặc dữ liệu của đối tượng đó (ví dụ:
self.name
,self.color
). Chúng là những "danh từ" mô tả đối tượng. -
Methods (Phương thức): Là các hàm thuộc về một class, dùng để định nghĩa các hành vi hoặc hành động mà đối tượng có thể thực hiện (ví dụ:
login()
,calculate_sum()
). Chúng là những "động từ" của đối tượng.
🔹 Ứng dụng trong Automation (Page Object Model):
Đây là ứng dụng quan trọng nhất của OOP trong automation.
-
Class:
HomePage
- Đại diện cho trang chủ. -
Attributes: Các locators của element trên trang (search_box , login_button ).
-
Methods: Các hành động người dùng có thể làm trên trang (
search_product(item_name)
,go_to_login_page()
).
class HomePage:
def __init__(self, page):
self.page = page
self.search_box = "#search"
self.login_button = "#login"
def search_product(self, item_name):
self.page.fill(self.search_box, item_name)
self.page.press(self.search_box, "Enter")
def go_to_login(self):
self.page.click(self.login_button)
5. Scope - "Phạm vi ảnh hưởng" của biến
Scope định nghĩa nơi một biến có thể được truy cập. Hiểu sai về scope là một nguyên nhân phổ biến gây ra lỗi.
-
Local Scope: Biến được tạo bên trong một hàm/phương thức. Nó chỉ tồn tại và có thể được sử dụng bên trong hàm đó mà thôi.
-
Global Scope: Biến được khai báo ở cấp cao nhất của file Python. Nó có thể được truy cập từ bất kỳ đâu trong file. (Nên hạn chế sử dụng biến global).
- Instance Scope: Biến được gắn với một đối tượng cụ thể thông qua
self
(ví dụ:self.name
). Nó tồn tại chừng nào đối tượng còn tồn tại và có thể được truy cập bởi mọi phương thức trong class đó.
global_variable = "Tôi ở khắp mọi nơi" # Global scope
class MyClass:
def __init__(self):
self.instance_variable = "Tôi thuộc về đối tượng" # Instance scope
def my_method(self):
local_variable = "Tôi chỉ ở trong hàm này" # Local scope
print(local_variable)
print(self.instance_variable)
print(global_variable)
obj = MyClass()
obj.my_method()
# Output:
# Tôi chỉ ở trong hàm này
# Tôi thuộc về đối tượng
# Tôi ở khắp mọi nơi
print(global_variable) # OK
print(obj.instance_variable) # OK
# print(local_variable) # Lỗi! NameError: name 'local_variable' is not defined
🔹 Ứng dụng trong Automation (Page Object Model):
BASE_URL = "https://example.com" # Global scope
class BaseTest:
def __init__(self, page):
self.page = page # Instance scope
def open_home(self):
self.page.goto(BASE_URL)
6. Mini Quiz 🎯
-
Đâu là "bản thiết kế" và đâu là "sản phẩm cụ thể": Class hay Object?
-
Phương thức đặc biệt nào sẽ tự động chạy khi một đối tượng được tạo?
-
Trong một class, từ khóa
self
đại diện cho điều gì? -
Nơi lưu trữ locator của element trong Page Object Model nên là Thuộc tính hay Phương thức?
-
Một biến được tạo bên trong một phương thức mà không có
self
đứng trước thì thuộc scope nào?
7. Bài tập tổng hợp cuối buổi
Bài 1: Lớp Product
(Sản phẩm) Viết một class Product
để quản lý thông tin sản phẩm.
-
__init__
: Nhận vàoname
,price
, vàquantity
(số lượng). -
Phương thức
get_total_price()
: Trả về tổng giá trị của sản phẩm đó (price * quantity
). -
Phương thức
display_info()
: In ra thông tin sản phẩm theo định dạng: "Sản phẩm: [Tên], Đơn giá: [Giá], Số lượng: [Số lượng], Tổng giá trị: [Tổng giá trị]".
Bài 2: Máy tính đơn giản Viết một class Calculator
.
-
Không cần
__init__
phức tạp. -
Phương thức
add(a, b)
: Trả về tổng của a và b. -
Phương thức
subtract(a, b)
: Trả về hiệu của a và b. -
Phương thức
multiply(a, b)
: Trả về tích của a và b. -
Phương thức
divide(a, b)
: Trả về thương của a và b. Dùngtry-except
để xử lý trường hợpb
bằng 0.
Bài 3: Automation - LoginPage
Object Tạo một class LoginPage
hoàn chỉnh (không cần chạy Selenium thật, chỉ cần viết code).
-
__init__(self, driver)
: Lưu lạidriver
. -
Thuộc tính:
username_locator
,password_locator
,login_button_locator
. -
Phương thức
enter_username(self, username)
: Giả lập việc tìm element username và gõusername
vào. Dùngprint()
để mô phỏng. -
Phương thức
enter_password(self, password)
: Tương tự cho password. -
Phương thức
click_login(self)
: Tương tự cho nút login. -
Phương thức
login(self, username, password)
: Gọi 3 phương thức trên theo đúng thứ tự để thực hiện toàn bộ quy trình đăng nhập.