اولین تست Selenium شما نباید به دانلود دستی ChromeDriver، مسیرهای مبهم سیستم یا جست‌وجوی ناپایدار Google وابسته باشد. یک شروع خوب باید سه چیز را از همان روز اول درست آموزش دهد: مدیریت خودکار Driver، انتظار شرطی به‌جای sleep و پاک‌سازی قطعی مرورگر. در این راهنما یک پروژه کوچک Python می‌سازیم که واقعاً با pytest اجرا می‌شود و پایه گسترش به یک Suite حرفه‌ای را دارد.

پاسخ کوتاه: Python و یک مرورگر را نصب کنید، در virtual environment دستور python -m pip install -U selenium pytest را اجرا کنید، تست زیر را بسازید و با pytest -q اجرا کنید. در Selenium جدید، webdriver.Chrome() به‌طور پیش‌فرض از Selenium Manager برای مدیریت Driver استفاده می‌کند؛ معمولاً نیازی به دانلود دستی ChromeDriver یا کتابخانه ثالث ندارید.

خروجی این آموزش: یک تست مستقل که صفحه نمونه رسمی Selenium را باز می‌کند، فرم را پر می‌کند، نتیجه را با Explicit Wait می‌خواند، assertion واقعی دارد و حتی در صورت شکست session مرورگر را می‌بندد.

Selenium WebDriver چیست؟

Selenium مجموعه‌ای متن‌باز برای خودکارسازی مرورگر وب است. اجزای اصلی اکوسیستم:

  • WebDriver: API کنترل مرورگر به‌صورت محلی یا Remote؛ تمرکز این مقاله.
  • Grid: اجرای Remote و موازی روی ماشین‌ها و مرورگرهای مختلف.
  • IDE: افزونه ضبط/پخش برای نمونه‌سازی و سناریوهای ساده.
  • Selenium Manager: مدیریت خودکار Driver و در برخی سناریوها browser؛ bindingها آن را به‌طور پیش‌فرض فراخوانی می‌کنند.

طبق مستندات رسمی WebDriver، مرورگر به شکل native و محلی یا از طریق Selenium Server کنترل می‌شود. Selenium خودِ Test Framework، گزارش مدیریتی، API Testing یا ابزار Performance نیست؛ معمولاً کنار pytest/JUnit/TestNG و ابزارهای دیگر قرار می‌گیرد.

Selenium برای چه چیزی مناسب است؟

  • Smoke و Regression رابط وب
  • مسیرهای بحرانی کاربر در چند مرورگر
  • فرم، ناوبری، احراز هویت و workflowهای UI
  • بررسی JavaScript/DOM از دید مرورگر واقعی
  • اجرای local، headless، CI یا Grid

برای تست مستقیم API، اپ موبایل native، دسکتاپ و بار سنگین ابزار دیگری لازم است. صفحه راهنمای اتوماسیون تست کمک می‌کند Selenium را فقط وقتی لایه UI واقعاً لازم است انتخاب کنید.

پیش‌نیازهای شروع Selenium با Python

  • Python ۳ نصب‌شده و در دسترس ترمینال
  • مرورگر Chrome یا Chromium سازگار؛ Firefox نیز قابل‌استفاده است
  • دسترسی اولیه شبکه برای نصب package و مدیریت Driver
  • یک ویرایشگر مانند VS Code یا PyCharm
  • آشنایی پایه با تابع، import، fixture و assertion در Python

برای کنترل نسخه‌ها، از virtual environment استفاده می‌کنیم. پروژه آموزشی را روی سامانه خود یا صفحه نمونه رسمی اجرا کنید؛ روی سایت‌های ثالث بدون مجوز، حساب یا داده واقعی تست خودکار نسازید.

گام ۱: ساخت پروژه و Virtual Environment

یک پوشه تازه بسازید و وارد آن شوید. سپس:

python -m venv .venv

فعال‌سازی در macOS/Linux:

source .venv/bin/activate

فعال‌سازی در Windows PowerShell:

.venv\Scripts\Activate.ps1

اگر فرمان سیستم شما python3 است، همان را جایگزین python کنید. بعد نسخه را کنترل کنید:

python --version
python -m pip --version

گام ۲: نصب Selenium و pytest

python -m pip install -U selenium pytest

نصب را بررسی کنید:

python -m pip show selenium
pytest --version

استفاده از python -m pip احتمال نصب package در Python متفاوت از virtual environment فعال را کاهش می‌دهد. برای پروژه واقعی نسخه وابستگی‌ها را در فایل lock/requirements کنترل کنید؛ در مقاله آموزشی از عدد نسخه ثابت استفاده نمی‌کنیم تا دستور سریعاً منقضی نشود.

گام ۳: Selenium Manager و Driver مرورگر

در Selenium مدرن، این خط برای شروع local معمولاً کافی است:

driver = webdriver.Chrome()

Selenium Manager همراه bindingها فراخوانی می‌شود و Driver سازگار را مدیریت می‌کند. مستند رسمی troubleshooting نیز توضیح می‌دهد که از Selenium ۴.۶ به بعد، Selenium می‌تواند Driver مناسب را دانلود کند. بنابراین این مسیرها برای شروع پیش‌فرض نیستند:

  • دانلود دستی ChromeDriver و کپی در PATH
  • هاردکد کردن مسیر Driver در کد
  • افزودن WebDriverManager ثالث فقط برای اولین اجرا

مدیریت دستی هنوز در شبکه بسته، cache سازمانی یا نیاز ویژه ممکن است لازم باشد. اگر تیم ایرانی به download host دسترسی ناپایدار دارد، cache، proxy مجاز یا Driver مدیریت‌شده در runner را سازمانی تنظیم کنید؛ مسیر شخصی هر لپ‌تاپ را داخل repository commit نکنید.

ساختار ساده پروژه

selenium-first-test/
├── .venv/
└── test_web_form.py

فایل .venv را وارد Git نکنید. بعداً می‌توانید pages/، tests/، conftest.py و فایل تنظیمات اضافه کنید؛ برای اولین تست، معماری سنگین لازم نیست.

گام ۴: نوشتن اولین تست Selenium با pytest

محتوای زیر را در test_web_form.py قرار دهید:

import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait


@pytest.fixture
def driver():
    browser = webdriver.Chrome()
    yield browser
    browser.quit()


def test_submit_web_form(driver):
    wait = WebDriverWait(driver, 10)

    driver.get("https://www.selenium.dev/selenium/web/web-form.html")
    assert "Web form" in driver.title

    text_box = wait.until(
        EC.visibility_of_element_located((By.NAME, "my-text"))
    )
    text_box.send_keys("Selenium")

    driver.find_element(By.CSS_SELECTOR, "button").click()

    message = wait.until(
        EC.visibility_of_element_located((By.ID, "message"))
    )
    assert message.text == "Received!"

این کد از صفحه نمونه پایدارِ خود Selenium استفاده می‌کند، نه موتور جست‌وجویی که محتوای آن با کشور، consent، کپچا و آزمایش‌های UI تغییر می‌کند. مراحل رسمی ساخت اولین script نیز در راهنمای رسمی Selenium آمده است؛ نمونه بالا آن را به یک تست pytest با fixture و Explicit Wait تبدیل می‌کند.

گام ۵: اجرای تست

pytest -q

خروجی موفق تقریباً چنین ساختاری دارد:

1 passed in ...s

در اولین اجرا ممکن است راه‌اندازی Driver کمی بیشتر زمان ببرد. برای دیدن نام تست و جزئیات بیشتر:

pytest -v

برای اجرای فقط همین تست:

pytest -v test_web_form.py::test_submit_web_form

کد اولین تست چه می‌کند؟

Fixture و چرخه عمر مرورگر

@pytest.fixture
def driver():
    browser = webdriver.Chrome()
    yield browser
    browser.quit()

قبل از تست session ساخته، با yield تحویل تست و بعد از پایان—حتی در شکست عادی تست—با quit() بسته می‌شود. quit() کل session و همه پنجره‌های آن را می‌بندد؛ close() فقط پنجره فعلی را هدف می‌گیرد.

driver.get("https://www.selenium.dev/selenium/web/web-form.html")
assert "Web form" in driver.title

get() URL را باز می‌کند. assertion عنوان تایید می‌کند در صفحه مورد انتظاریم. بدون assertion، اسکریپت فقط automation است، نه test با oracle روشن.

Locator و پیدا کردن عنصر

(By.NAME, "my-text")
(By.CSS_SELECTOR, "button")
(By.ID, "message")

Locator یک قرارداد برای یافتن عنصر در DOM است. اولویت عملی با صفت پایدار و معنایی است: ID یکتا، نام کنترل یا data-testid توافق‌شده. کلاس صرفاً ظاهری، متن ترجمه‌شونده و XPath مطلق معمولاً شکننده‌اند. CSS و XPath هر دو ابزار معتبرند؛ مسئله پایداری selector است، نه مسابقه مطلق سرعت.

Explicit Wait

wait.until(
    EC.visibility_of_element_located((By.ID, "message"))
)

کد تا برآورده‌شدن شرط visibility یا پایان timeout polling می‌کند. مستند رسمی Expected Conditions شرط‌هایی مانند وجود، visibility، متن و stale شدن را پوشش می‌دهد.

Assertion نتیجه

assert message.text == "Received!"

انتظار دقیق است و در شکست، مقدار واقعی قابل بررسی می‌ماند. assertionهای ضعیف مانند «عنصر وجود دارد» ممکن است خطای متن یا state را نبینند.

Wait در Selenium؛ چرا sleep نزنیم؟

این کد ظاهراً کار می‌کند اما شکننده است:

import time

time.sleep(5)
element = driver.find_element(By.ID, "message")

اگر عنصر در نیم ثانیه آماده شود، ۴٫۵ ثانیه تلف می‌شود؛ اگر در شش ثانیه آماده شود، تست شکست می‌خورد. Explicit Wait منتظر یک state واقعی می‌ماند.

مستند رسمی Waiting Strategies هشدار می‌دهد Implicit و Explicit Wait را با هم مخلوط نکنید، چون زمان‌های غیرقابل‌پیش‌بینی می‌سازد. برای مجموعه تازه، یک راهبرد سازگار انتخاب کنید؛ این راهنما Explicit Wait را برای شرط‌های مهم ترجیح می‌دهد.

شرط‌های پرکاربرد

  • presence_of_element_located: عنصر در DOM هست، نه لزوماً قابل‌دیدن
  • visibility_of_element_located: عنصر موجود و قابل‌دیدن است
  • element_to_be_clickable: visible و enabled است
  • text_to_be_present_in_element: متن مورد انتظار ظاهر شده
  • staleness_of: مرجع عنصر قدیمی از DOM خارج شده

شرط را با هدف انتخاب کنید. «clickable» تضمین نمی‌کند overlay عنصر را نپوشانده یا اقدام کسب‌وکار کامل شده باشد؛ بعد از click منتظر state نتیجه نیز بمانید.

اجرای Headless و گزینه‌های مرورگر

برای CI می‌توانید headless اجرا کنید:

from selenium import webdriver

options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--window-size=1440,900")

driver = webdriver.Chrome(options=options)

اندازه پنجره را صریح کنید تا responsive layout و screenshot قابل‌تکرارتر شود. نتیجه headless و headed را برای مسیر بحرانی مقایسه کنید؛ همه تفاوت‌های GPU، فونت یا محیط به Selenium مربوط نیستند.

اجرای همان تست روی Firefox

Fixture را به این شکل عوض کنید:

@pytest.fixture
def driver():
    browser = webdriver.Firefox()
    yield browser
    browser.quit()

Selenium Manager در حالت معمول Driver لازم را مدیریت می‌کند. Cross-Browser واقعی فقط تغییر constructor نیست؛ ماتریس browser/OS/version باید از کاربران و تعهد پشتیبانی بیاید. مقاله تست Cross Browser این ماتریس را توضیح می‌دهد.

چگونه Locator پایدار بنویسیم؟

انتخاب نمونه ارزیابی
ID یکتا By.ID, "submit-order" خوب اگر قرارداد پایدار باشد
data-testid [data-testid='checkout'] قرارداد صریح تست، نیازمند همکاری توسعه
Name/role قابل‌معنا By.NAME, "phone" خوانا و معمولاً پایدار
متن فارسی //button[.='پرداخت'] برای رفتار متنی مفید، در چندزبان شکننده
کلاس CSS ظاهری .btn-blue-xl با redesign می‌شکند
XPath مطلق /html/body/div[2]/... بسیار وابسته به ساختار

Selector پایدار یک مسئله testability است. با توسعه‌دهنده روی attribute و component contract توافق کنید. XPath نسبی و CSS هر دو می‌توانند خوب یا بد نوشته شوند.

خطاهای رایج Selenium و علت آن‌ها

NoSuchElementException

عنصر در لحظه جست‌وجو پیدا نشده است. URL/frame/window، locator و انتظار بارگذاری را بررسی کنید. فوراً timeout را افزایش ندهید؛ ممکن است صفحه اشتباه یا selector منقضی باشد.

StaleElementReferenceException

DOM تغییر کرده و reference قبلی دیگر معتبر نیست. پس از render مجدد عنصر را دوباره locate کنید و روی state مناسب wait بگذارید. نگهداری WebElement در Page Object برای مدت طولانی ریسک stale می‌سازد.

ElementClickInterceptedException

overlay، modal، header چسبان یا animation مانع click است. screenshot و DOM را ببینید، منتظر بسته‌شدن overlay بمانید و از JavaScript click به‌عنوان میان‌بُر پیش‌فرض استفاده نکنید؛ ممکن است رفتار واقعی کاربر را دور بزند.

Unable to Locate Driver

Selenium را به‌روز، دسترسی شبکه/Proxy و نصب مرورگر را بررسی و logging Selenium Manager را فعال کنید. اگر شبکه سازمانی download را مسدود می‌کند، Driver تاییدشده را در PATH یا cache استاندارد runner قرار دهید، نه مسیر محلی داخل test.

TimeoutException

شرط در بازه تعیین‌شده محقق نشده است. مقدار واقعی صفحه، network request و لاگ را ثبت کنید. بالا بردن سراسری timeout می‌تواند نقص کارایی یا انتظار اشتباه را پنهان کند.

نکات Selenium برای وب‌اپ فارسی

  • تست را روی RTL، viewport موبایل و فونت fallback اجرا کنید.
  • اعداد فارسی/لاتین، جداکننده، ریال/تومان و تاریخ شمسی را با داده کنترل‌شده بسنجید.
  • برای متن ترجمه‌شونده selector مستقل از locale داشته باشید؛ خود ترجمه را در assertion جدا بررسی کنید.
  • OTP و درگاه را با API test setup یا Sandbox کنترل‌شده آماده کنید؛ شماره و تراکنش واقعی را در کد نگذارید.
  • Artifact شامل cookie، token، شماره تلفن یا داده مالی را ماسک کنید.
  • با محدودیت دانلود Driver، cache و runner داخلی را پیش‌بینی کنید. راهنمای ابزارهای جایگزین برای محدودیت‌های ایران نکات دسترسی را تکمیل می‌کند.

از اولین Script تا Test Suite

پس از پایدار شدن چند تست، این مسیر را طی کنید:

  1. Fixture مرورگر را به conftest.py منتقل کنید.
  2. base URL، browser و headless را با config/CLI کنترل کنید.
  3. داده تست را با factory و API بسازید، نه زنجیره UI.
  4. Page/Component Object را برای رفتارهای تکراری اضافه کنید.
  5. test را مستقل و parallel-safe نگه دارید.
  6. Screenshot/log/network را فقط در شکست ذخیره کنید.
  7. markهای smoke/regression و timeout policy بسازید.
  8. تست‌های کم‌ارزش را به API/Integration منتقل کنید.
  9. در CI روی هر PR مجموعه سریع و زمان‌بندی‌شده مجموعه گسترده را اجرا کنید.

برای معماری Page Object، مقاله راهنمای POM پایدار و برای تصمیم گسترده‌تر Framework، مقاله انتخاب چارچوب اتوماسیون را بخوانید.

اجرای Selenium در CI

حداقل نیازهای CI:

  • Python و dependencyهای نسخه‌بندی‌شده
  • مرورگر/Driver قابل‌مدیریت یا image تاییدشده
  • Headless و window size ثابت
  • timeout و CPU/RAM کافی
  • artifact شکست و خروجی JUnit
  • secret امن و داده غیرواقعی
  • عدم retry بی‌نهایت تست Flaky

برای نمونه pipeline، مقاله راه‌اندازی CI/CD برای تست‌های خودکار مسیر بعدی است.

اشتباهات رایج مبتدیان Selenium

  • دانلود دستی Driver از آموزش قدیمی: مدیریت نسخه روی هر سیستم می‌شکند.
  • استفاده از Google به‌عنوان صفحه تمرین: کپچا، consent و locale تست را ناپایدار می‌کند.
  • sleep ثابت: تست کند و Flaky می‌شود.
  • نداشتن assertion: automation اجرا می‌شود اما چیزی را تایید نمی‌کند.
  • بسته‌نشدن browser: process و منابع باقی می‌مانند.
  • XPath مطلق و کلاس ظاهری: تغییر کوچک DOM صد تست را می‌شکند.
  • تست‌های وابسته به ترتیب: یک شکست زنجیره‌ای کل suite را قرمز می‌کند.
  • ورود از UI در هر تست: suite کند؛ setup API/session مناسب‌تر است.
  • POM بزرگ از روز اول: معماری قبل از شناخت رفتار پیچیده می‌شود.
  • گرفتن screenshot همه‌جا: artifact سنگین و حاوی داده حساس می‌شود.

چک‌لیست اولین تست Selenium

  • virtual environment فعال است.
  • Selenium و pytest از همان Python نصب شده‌اند.
  • webdriver.Chrome() بدون مسیر هاردکد Driver استفاده می‌شود.
  • صفحه تمرین پایدار و مجاز است.
  • تست assertion معنادار دارد.
  • Explicit Wait روی state واقعی استفاده شده است.
  • Implicit و Explicit Wait مخلوط نشده‌اند.
  • Fixture همیشه quit() را اجرا می‌کند.
  • Locator به attribute پایدار متکی است.
  • داده حساس در کد و artifact نیست.
  • تست مستقل و با pytest -q قابل‌تکرار است.

سؤالات متداول Selenium WebDriver

آیا هنوز باید ChromeDriver را دستی دانلود کنم؟

معمولاً خیر. Selenium جدید از Selenium Manager استفاده می‌کند. در شبکه بسته یا سیاست سازمانی می‌توانید Driver را مدیریت‌شده در PATH/cache قرار دهید، اما مسیر دستی پیش‌فرض آموزش نیست.

برای شروع Selenium، Python بهتر است یا Java؟

هر دو binding رسمی و اکوسیستم قوی دارند. زبانی را انتخاب کنید که تیم محصول می‌شناسد و در CI پشتیبانی می‌شود. Python برای شروع کوتاه‌تر است؛ Java در بسیاری از تیم‌های JVM طبیعی‌تر است.

چرا تست من گاهی Pass و گاهی Fail می‌شود؟

عامل رایج timing، داده مشترک، محیط، animation یا selector ناپایدار است. sleep و retry را اضافه نکنید؛ state مورد انتظار، evidence و استقلال داده را اصلاح کنید.

Implicit Wait بهتر است یا Explicit Wait؟

Implicit روی همه findها اثر سراسری دارد؛ Explicit برای شرط مشخص polling می‌کند. برای UI پویا Explicit کنترل بیشتری می‌دهد. طبق مستند رسمی آن‌ها را مخلوط نکنید.

آیا Selenium برای تست موبایل مناسب است؟

برای وب responsive در مرورگر بله؛ برای اپ native/hybrid موبایل به ابزار مخصوص مانند Appium یا راهکار platform نیاز دارید. شبیه‌سازی viewport جای دستگاه واقعی را کامل نمی‌گیرد.

جمع‌بندی

شروع درست Selenium ساده است: environment ایزوله، binding رسمی، Selenium Manager، صفحه نمونه پایدار، pytest، Explicit Wait و assertion واقعی. وقتی اولین تست قابل‌تکرار شد، به‌تدریج config، داده API، Page Object و CI را اضافه کنید. هدف نهایی باز کردن خودکار مرورگر نیست؛ ساختن بازخوردی است که تیم به آن اعتماد کند.

دیدگاهتان را بنویسید