Automation Với Selenium: Xử Lý Popup, Ajax Và Multi-Tab Cho Fresher

5 tháng trước · 17 phút đọc
Từ manual testing sang automation: bắt đầu từ đâu?
Bạn đang test tay một e-commerce. Mỗi sprint, dev deploy tính năng mới - bạn lại phải click qua 50 test case, verify popup khuyến mãi, kiểm tra ajax load sản phẩm, đóng tab thanh toán... Sau 3 tháng, cảm giác bắt đầu mệt.
Đó là lúc automation testing có ý nghĩa thật sự, không phải vì nó "hot" hay vì JD nào cũng ghi.
Selenium WebDriver là tool automation web phổ biến nhất hiện tại. Ý tưởng đơn giản: thay vì bạn ngồi click, Selenium điều khiển browser giả lập đúng thao tác người dùng - click, nhập text, scroll, chuyển tab. Dev không biết đó là bot hay người thật.
Selenium điều khiển browser y hệt người dùng thật - không có gì khác biệt với server
Vì sao fresher nên học Selenium ngay sau khi nắm manual? Vì:
- {{fact:fact_0}} automation tester có mức lương cao hơn manual tester cùng kinh nghiệm
- Portfolio có script automation thực chiến tăng đáng kể cơ hội qua vòng CV
- Kỹ năng chuyển được sang Playwright, Cypress sau này - nền tảng giống nhau
Bài này mình hướng dẫn từ cài đặt đến các kỹ thuật thực chiến hay gặp nhất trong web phức tạp. Không cần biết code sâu - biết Python cơ bản là đủ.
Cài đặt môi trường: đừng bỏ qua bước này
Nhiều bạn rush qua phần cài đặt rồi loay hoay mãi vì lỗi version. Mình sẽ liệt kê đúng thứ tự.
Bạn cần:
- Python 3.8+ (khuyến nghị 3.10 hoặc 3.11)
- Chrome browser (bản mới nhất)
- ChromeDriver khớp version Chrome
- Thư viện
seleniumvàwebdriver-manager
Cài qua terminal:
Test nhanh xem môi trường chạy không:
webdriver-manager tự match version ChromeDriver - tiết kiệm 80% thời gian setup
Nếu in ra được "Google" - môi trường OK. Phần lớn lỗi setup đến từ 2 nguyên nhân:
Lỗi chromedriver not found: Bạn chưa cài webdriver-manager hoặc pip install thất bại. Chạy lại pip install --upgrade webdriver-manager.
Lỗi SessionNotCreatedException: Version ChromeDriver không khớp Chrome. Dùng webdriver-manager là cách dứt điểm nhất - nó tự detect.
Mình khuyên bạn không dùng headless mode (ẩn browser) khi mới học. Thấy browser chạy giúp bạn debug nhanh hơn nhiều. Sau khi script ổn định rồi mới chuyển headless để chạy CI/CD.
Locator và thao tác cơ bản: nền tảng của mọi script
Trước khi xử lý popup hay Ajax, bạn cần nắm cách Selenium "tìm" element trên trang. Đây là phần mình thấy nhiều fresher hiểu sai nhất.
Selenium tìm element qua locator - giống như bạn mô tả địa chỉ nhà cho shipper. Có 4 locator dùng nhiều nhất:
| Locator | Dùng khi nào | Ví dụ |
|---|---|---|
By.ID |
Element có id duy nhất | By.ID, "username" |
By.CSS_SELECTOR |
Flexible, ngắn gọn | By.CSS_SELECTOR, ".btn-primary" |
By.XPATH |
Element không có id/class | By.XPATH, "//button[text()='Đặt hàng']" |
By.NAME |
Input form | By.NAME, "password" |
Ưu tiên: ID > CSS Selector > XPath. XPath chỉ dùng khi không còn cách nào khác - nó brittle (dễ vỡ khi dev thay đổi HTML).
Ưu tiên CSS Selector hơn XPath - ít vỡ hơn khi dev refactor HTML
Site demo nopcommerce.com là e-commerce mở để test - mình dùng xuyên suốt bài này vì nó có đầy đủ popup, ajax, multi-tab để luyện.
Lưu ý thực chiến: Sau find_element, gọi .clear() trước .send_keys() với input field. Thiếu bước này, text cũ còn trong field sẽ gây lỗi logic mà khó debug.
Xử lý wait và Ajax: lỗi phổ biến nhất của fresher
Mình từng nhận bug report từ một bạn: "Script chạy được hôm qua, hôm nay lỗi không rõ lý do." Kiểm tra lại - bạn ấy dùng time.sleep(2) cứng ở mọi chỗ. Hôm qua server nhanh, hôm nay chậm hơn 1 giây - script vỡ.
time.sleep() là anti-pattern trong automation. Thay vào đó, Selenium có Explicit Wait - chờ đúng đến khi element sẵn sàng, không chờ thừa.
time.sleep() cố định = script dễ vỡ. Explicit Wait = chờ thông minh theo trạng thái thật
Các expected_conditions hay dùng:
presence_of_element_located- Element có trong DOM (chưa cần visible)element_to_be_clickable- Element visible VÀ có thể clicktext_to_be_present_in_element- Chờ text xuất hiện trong elementinvisibility_of_element_located- Chờ loading spinner biến mấturl_contains- Chờ URL chứa chuỗi nhất định (sau redirect)
Pattern chờ spinner Ajax biến mất:
Rule của mình: không có time.sleep() trong production script. Nếu thấy mình muốn viết sleep, dừng lại và nghĩ xem Explicit Wait nào phù hợp hơn.
Xử lý popup: 3 loại khác nhau, 3 cách xử lý khác nhau
Popup trong web có 3 dạng hoàn toàn khác nhau về cơ chế. Fresher hay nhầm lẫn 3 loại này nên script không chạy.
1. Browser alert (JavaScript alert/confirm/prompt)
Đây là popup của browser, không phải của web app. Trông khác - không có CSS custom, chỉ có nút OK/Cancel.
2. Modal dialog (popup CSS của web app)
Loại phổ biến nhất trong e-commerce: popup khuyến mãi, confirm xóa sản phẩm, popup đăng nhập nhanh. Đây chỉ là HTML element bình thường - dùng find_element như thường.
3. Toast notification (thông báo thoáng qua)
Loại khó nhất: xuất hiện rồi tự biến mất sau 3-5 giây. Phải verify ngay lập tức.
3 loại popup, 3 cơ chế hoàn toàn khác nhau - nhầm là script vỡ ngay
Xử lý multi-tab và iframe: hai kỹ năng ít người để ý
Multi-tab: thanh toán mở tab mới
Scenario hay gặp: click "Thanh toán qua VNPay" - trang mở tab mới. Selenium vẫn focus ở tab cũ nên mọi thao tác sau đó đều fail.
Quên switch_to.window() là lỗi phổ biến nhất khi test thanh toán multi-tab
iframe: form nhúng trong trang
iframe là trang web nhúng trong trang web khác. Hay gặp ở: form thanh toán thẻ (Stripe, PayPal), bản đồ Google Maps, chatbox. Selenium không thể find element trong iframe nếu chưa switch vào.
Pattern ghi nhớ: Mỗi lần vào iframe bằng switch_to.frame(), ra ngoài bằng switch_to.default_content(). Quên dòng cuối - mọi find_element tiếp theo đều fail.
Drag & drop và thao tác nâng cao
Drag & drop hay xuất hiện trong: sắp xếp ảnh sản phẩm, kéo item vào giỏ, upload file qua drag. Selenium có ActionChains để mô phỏng chuỗi thao tác phức tạp.
ActionChains gom nhiều thao tác thành 1 chuỗi - giống đúng hành vi người dùng thật
Khi nào dùng ActionChains:
- Hover trigger dropdown
- Drag & drop
- Double click / right click
- Click và giữ (hold)
- Keyboard shortcut (Ctrl+A, Ctrl+C...)
Lưu ý quan trọng: Một số site dùng JavaScript library riêng cho drag & drop (jQuery UI, Sortable.js) - ActionChains.drag_and_drop() có thể không work. Lúc đó dùng JavaScript execution:
Template script thực chiến cho e-commerce
Mình tổng hợp tất cả kỹ thuật trên thành 1 script hoàn chỉnh - test luồng mua hàng trên nopcommerce.com. Đây là loại script mà bạn có thể đưa vào portfolio khi xin việc.
Script có pytest fixture tự động teardown - chuyên nghiệp hơn nhiều so với try/finally thủ công
Tải template đầy đủ tại đây: Tải template script Selenium E-commerce
Script này dùng pytest thay vì chạy thẳng Python. Lý do: pytest tự quản lý pass/fail, generate report, dễ tích hợp CI/CD sau này. Nếu bạn chưa quen pytest, chạy pip install pytest và thay print bằng assert - hiệu quả ngay.
Về portfolio: bạn không cần script 500 dòng để gây ấn tượng. 3-4 test function cover các scenario khác nhau (happy path + edge case + negative test) + README giải thích cách chạy = đủ để phân biệt với 80% ứng viên.
Lỗi thường gặp và cách debug nhanh
Bốn lỗi dưới đây chiếm phần lớn thời gian debug của fresher. Mình liệt kê để bạn nhận ra ngay thay vì Google 30 phút.
NoSuchElementException - Element không tìm thấy
Khả năng cao nhất: element chưa load (thiếu wait), hoặc đang trong iframe chưa switch vào, hoặc locator sai. Debug: mở DevTools, paste selector vào console: document.querySelector('.your-selector') - nếu trả về null thì selector sai.
ElementNotInteractableException - Element có trong DOM nhưng không click được
Thường gặp khi element bị che bởi element khác (overlay, modal, sticky header). Fix:
StaleElementReferenceException - Element đã cũ, DOM đã thay đổi
Xảy ra khi: find element → trang reload/Ajax cập nhật DOM → click element cũ. Fix: find lại element ngay trước khi dùng.
TimeoutException - Chờ quá lâu không thấy element
Debug theo thứ tự: (1) Mở tay, test bằng mắt xem element có xuất hiện không. (2) Kiểm tra selector có đúng không. (3) Tăng timeout thử. (4) Xem element có trong iframe không.
Debug theo đúng thứ tự - tiết kiệm hơn nhiều so với trial and error
Một trick hữu ích: khi không chắc script đang ở đâu, thêm driver.save_screenshot('debug.png') - xem browser đang thấy gì lúc lỗi. Mình dùng trick này nhiều hơn bất kỳ tool debug nào khác.
Bước tiếp theo cho portfolio của bạn
Script chạy được chưa phải là xong. Để portfolio thuyết phục, bạn cần thêm 2 thứ: test report và Page Object Model (POM).
Test report với pytest-html:
Chạy xong, file report.html có đầy đủ: số test pass/fail, thời gian chạy, log lỗi. Upload lên GitHub cùng script - recruiter xem được ngay mà không cần chạy code.
Page Object Model - cấu trúc code chuyên nghiệp:
Thay vì viết selector rải rác khắp file, POM gom selector và action của mỗi page vào 1 class:
POM tách selector ra khỏi test logic - dev thay CSS class, bạn chỉ sửa 1 chỗ thay vì 10 chỗ
Lộ trình học tiếp sau bài này:
- Pytest cơ bản - parametrize, fixture, conftest.py
- Page Object Model - cấu trúc project thực tế
- CI/CD với GitHub Actions - script chạy tự động mỗi khi push code
- Playwright - tool mới hơn, API giống Selenium nhưng nhanh hơn và ít flaky hơn
Bạn không cần học hết ngay. Với fresher xin việc, mục tiêu thực tế là: 1 project GitHub có 10-15 test case chạy được, có README, có báo cáo - đó là cái 80% ứng viên không có.
Comment bên dưới nếu bạn gặp lỗi khi chạy script - mình trả lời từng bạn. Chúc luyện tập vui!
