آموزش کامل Playwright در ASP.NET Core — از صفر تا صد
⚡ خلاصه سریع — اصل کاریها
اینها مهمترین چیزهایی هستن که اگه هیچی ندونی، فقط اینا رو بدونی کافیه:
- نکته ۱: Playwright ابزار رسمی مرورگر خودکار برای .NET است و تستهای End-to-End واقعی را انجام میدهد.
- نکته ۲: پکیج
Microsoft.Playwrightرا نصب و بلافاصله باplaywright.ps1 installمرورگرها را دانلود کن. - نکته ۳: نقطه شروع ساخت
IPlaywrightو سپسIBrowserاز طریقPlaywright.CreateAsync()وChromium.LaunchAsync()است. - نکته ۴: تمام تعاملات روی
IPageصورت میگیرد؛ ازGotoAsyncبرای ناوبری،FillAsyncبرای ورود متن وClickAsyncبرای کلیک استفاده کن. - نکته ۵: از Locatorهای مدرن مثل
GetByRole،GetByLabelوGetByTestIdبهجای Selectorهای CSS شکننده استفاده کن. - نکته ۶: برای اتصال Playwright به ASP.NET Core، برنامه را روی یک پورت واقعی مانند
http://localhost:5000اجرا کن. - نکته ۷: همیشه منابع را با
DisposeAsyncیاawait usingببند؛ بازماندن Browser باعث قفل مرورگر و هدررفت حافظه میشود. - نکته ۸: حالت Headless برای CI/CD و حالت Headed برای دیباگ محلی کاربردی است.
۳ اشتباه رایج که باید ازشون پرهیز کنی:
- فراموش کردن نصب مرورگرها بعد از افزودن پکیج.
- استفاده از Selectorهای شکننده مثل
div > spanبهجای Locatorهای مبتنی بر نقش یا متن. - اتصال به
TestServerبهجای Kestrel واقعی؛ مرورگر باید به یک URL شبکهای دسترسی داشته باشد.
اگر فقط ۵ دقیقه وقت داری، اینها رو یاد بگیر:
- ساخت
IPlaywrightوIBrowserبرای باز کردن مرورگر. - ناوبری و تعامل با
page.GotoAsyncوpage.ClickAsync. - نصب مرورگرها با
playwright.ps1 install.
۱. مقدمه
این موضوع چیه؟
Playwright یک کتابخانه اتوماسیون مرورگر متنباز از مایکروسافت است که به شما اجازه میدهد مرورگرهای Chromium، Firefox و WebKit را بهصورت برنامهنویسی کنترل کنید. نسخه رسمی .NET آن با نام Microsoft.Playwright منتشر میشود و میتواند مستقیماً در پروژههای ASP.NET Core برای تست رابط کاربری (UI) و سناریوهای End-to-End استفاده شود.
چرا باید یادش بگیری؟
- برخلاف Selenium، Playwright انتظار خودکار (auto-waiting) دارد؛ یعنی قبل از کلیک یا تایپ، صبر میکند تا عنصر واقعاً قابل تعامل شود.
- Locatorهای قویتر و پایدارتر (GetByRole، GetByText، GetByTestId) دارد.
- جداسازی Contextها باعث تستهای موازی امن میشود.
- امکانات حرفهای مثل Trace، Video Recording و Network Mocking بهصورت داخلی فراهم است.
کجا استفاده میشود؟
- تست فرمهای لاگین و احراز هویت
- تست جریانهای تجاری در برنامههای Razor Pages و MVC
- تست رابط کاربری Blazor Server/WebAssembly
- تست رگرسیون رابط کاربری در CI/CD
- اسکرینشات گرفتن و دیباگ صفحات
۲. پیشنیازها
چه چیزهایی رو باید از قبل بدونی؟
- آشنایی با زبان C# و برنامهنویسی async/await
- آشنایی اولیه با ASP.NET Core (MVC یا Razor Pages)
- مفاهیم ابتدایی تستنویسی (ترجیحاً xUnit)
- HTML و CSS در حد تشخیص عناصر صفحه
ابزارهای لازم
- .NET SDK نسخه 8 یا بالاتر
- Visual Studio / Rider / VS Code
- PowerShell یا Bash
- مرورگر Chromium یا Firefox برای اجرای تستها
۳. نصب و راهاندازی
۳.۱ ساخت پروژه تست
یک پروژه xUnit برای تستها بساز:
dotnet new xunit -n PlaywrightDemo.Tests
cd PlaywrightDemo.Tests
۳.۲ نصب پکیج Playwright
dotnet add package Microsoft.Playwright
۳.۳ نصب مرورگرها
بعد از افزودن پکیج، باید یک بار پروژه را Build کنی تا اسکریپت playwright.ps1 در پوشه خروجی ساخته شود:
dotnet build
سپس مرورگرها را نصب کن:
pwsh bin/Debug/net8.0/playwright.ps1 install
اگر فقط Chromium نیاز داری:
pwsh bin/Debug/net8.0/playwright.ps1 install chromium
در لینوکس/CI معمولاً نیاز به نصب وابستگیهای سیستمی هم داری:
pwsh bin/Debug/net8.0/playwright.ps1 install --with-deps
توجه: مسیر
net8.0بسته به نسخه .NET تو ممکن استnet6.0،net7.0یاnet9.0باشد.
۳.۴ اولین پروژه ساده
برای تست سریع، یک برنامه Console بساز:
dotnet new console -n PlaywrightIntro
cd PlaywrightIntro
dotnet add package Microsoft.Playwright
dotnet build
pwsh bin/Debug/net8.0/playwright.ps1 install chromium
محتویات Program.cs را اینطور بنویس:
using Microsoft.Playwright;
// ساخت Playwright
using var playwright = await Playwright.CreateAsync();
// باز کردن مرورگر Chromium
await using var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
{
Headless = false // برای دیدن مرورگر
});
// ساخت یک صفحه جدید
var page = await browser.NewPageAsync();
// رفتن به یک URL
await page.GotoAsync("https://example.com");
Console.WriteLine("Title: " + await page.TitleAsync());
توضیح:
Playwright.CreateAsync()نقطه ورود اصلی است.Chromium.LaunchAsync()مرورگر را اجرا میکند.NewPageAsync()یک تب جدید باز میکند.GotoAsync()به یک آدرس میرود.TitleAsync()عنوان صفحه را برمیگرداند.
۴. مفاهیم پایه
۴.۱ IPlaywright
IPlaywright شیء اصلی و نقطه شروع است. با استفاده از آن میتوانی به انواع مرورگر (Chromium، Firefox، WebKit) دسترسی پیدا کنی:
using var playwright = await Playwright.CreateAsync();
var chromium = playwright.Chromium;
var firefox = playwright.Firefox;
var webkit = playwright.Webkit;
۴.۲ IBrowser و IBrowserContext
IBrowser یک نمونه مرورگر را نشان میدهد. اما IBrowserContext یک جلسه ایزوله با کوکیها، حافظه محلی و تبهای مخصوص به خود است. هر Context مانند یک پروفایل جداگانه مرورگر عمل میکند:
await using var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
{
Headless = true
});
// ساخت Context جداگانه
await using var context = await browser.NewContextAsync();
// ساخت صفحه در داخل Context
var page = await context.NewPageAsync();
استفاده از Context به شما اجازه میدهد تستهای موازی بدون تداخل کوکی و Session اجرا شوند.
۴.۳ IPage
IPage یک تب یا صفحه وب را نشان میدهد. مهمترین متدهای آن:
| متد | کاربرد |
|---|---|
GotoAsync() |
رفتن به آدرس |
ClickAsync() |
کلیک روی عنصر |
FillAsync() |
پر کردن ورودی متن |
TitleAsync() |
گرفتن عنوان صفحه |
ContentAsync() |
گرفتن HTML صفحه |
ScreenshotAsync() |
عکس گرفتن |
۴.۴ Locator
Locator روشی برای پیدا کردن عناصر صفحه است. Locatorهای مدرن نسبت به Selectorهای CSS پایدارترند:
// با متن
var link = page.GetByText("Home");
// با نقش (Role)
var button = page.GetByRole(AriaRole.Button, new() { Name = "Login" });
// با Label
var usernameInput = page.GetByLabel("Username");
// با data-testid
var submit = page.GetByTestId("submit-button");
// با Selector CSS (کمتر توصیه میشود)
var oldWay = page.Locator("div > span.login");
۴.۵ انتظار خودکار (Auto-waiting)
برخلاف Selenium که باید دستی Thread.Sleep یا WebDriverWait بنویسی، Playwright قبل از عملیاتها منتظر میماند تا عنصر قابل مشاهده و فعال شود:
// این کلیک منتظر میماند تا دکمه واقعاً قابل کلیک باشد
await page.GetByRole(AriaRole.Button, new() { Name = "Login" }).ClickAsync();
اگر عنصر در مدتزمان پیشفرض (معمولاً ۳۰ ثانیه) پیدا نشود، خطای Timeout میگیرید.
۵. مثالهای کد ساده
۵.۱ رفتن به یک صفحه و چاپ عنوان
using Microsoft.Playwright;
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
{
Headless = true
});
var page = await browser.NewPageAsync();
await page.GotoAsync("https://example.com");
Console.WriteLine(await page.TitleAsync());
۵.۲ پر کردن فرم و کلیک
var page = await browser.NewPageAsync();
await page.GotoAsync("https://demo.example.com/login");
// پر کردن فیلدها با label
await page.GetByLabel("Username").FillAsync("admin");
await page.GetByLabel("Password").FillAsync("123456");
// کلیک روی دکمه Login
await page.GetByRole(AriaRole.Button, new() { Name = "Login" }).ClickAsync();
۵.۳ گرفتن اسکرینشات
await page.ScreenshotAsync(new PageScreenshotOptions
{
Path = "screenshot.png",
FullPage = true // تمام صفحه، نه فقط قسمت قابل مشاهده
});
۵.۴ انتظار برای نمایش یک عنصر
using static Microsoft.Playwright.Assertions;
// بعد از لاگین منتظر نمایش متن Welcome باش
await Expect(page.GetByText("Welcome")).ToBeVisibleAsync();
۵.۵ کار با ASP.NET Core روی localhost
await page.GotoAsync("http://localhost:5000");
// کلیک روی لینک Home
await page.GetByRole(AriaRole.Link, new() { Name = "Home" }).ClickAsync();
۶. مثال واقعی و کاربردی
سناریو: تست لاگین در ASP.NET Core MVC
قدم ۱: ساخت برنامه ASP.NET Core MVC
dotnet new sln -n PlaywrightDemo
dotnet new mvc -n PlaywrightDemo.Web
dotnet new xunit -n PlaywrightDemo.Tests
dotnet sln add PlaywrightDemo.Web PlaywrightDemo.Tests
قدم ۲: افزودن صفحه لاگین ساده
فایل Models/LoginViewModel.cs:
namespace PlaywrightDemo.Web.Models;
public class LoginViewModel
{
public string Username { get; set; }
public string Password { get; set; }
}
فایل Controllers/AccountController.cs:
using Microsoft.AspNetCore.Mvc;
using PlaywrightDemo.Web.Models;
namespace PlaywrightDemo.Web.Controllers;
public class AccountController : Controller
{
[HttpGet]
public IActionResult Login()
{
return View();
}
[HttpPost]
public IActionResult Login(LoginViewModel model)
{
if (model.Username == "admin" && model.Password == "123456")
{
ViewData["WelcomeMessage"] = "خوش آمدید، admin!";
return View("Welcome");
}
ModelState.AddModelError("", "Invalid credentials");
return View(model);
}
}
فایل Views/Account/Login.cshtml:
@model LoginViewModel
<h1>Login</h1>
<form asp-action="Login" method="post">
<div>
<label asp-for="Username">Username</label>
<input asp-for="Username" />
</div>
<div>
<label asp-for="Password">Password</label>
<input asp-for="Password" type="password" />
</div>
<button type="submit">Login</button>
</form>
فایل Views/Account/Welcome.cshtml:
<h1>@ViewData["WelcomeMessage"]</h1>
قدم ۳: آمادهسازی پروژه تست
cd PlaywrightDemo.Tests
dotnet add package Microsoft.Playwright
dotnet build
pwsh bin/Debug/net8.0/playwright.ps1 install chromium
قدم ۴: ساخت Fixture برای مدیریت مرورگر
فایل PlaywrightFixture.cs:
using Microsoft.Playwright;
namespace PlaywrightDemo.Tests;
public class PlaywrightFixture : IAsyncLifetime
{
public IPlaywright Playwright { get; private set; }
public IBrowser Browser { get; private set; }
public async Task InitializeAsync()
{
Playwright = await Playwright.CreateAsync();
Browser = await Playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
{
Headless = true, // در CI معمولاً true
SlowMo = 100 // فقط برای دیباگ محلی
});
}
public async Task DisposeAsync()
{
await Browser.DisposeAsync();
Playwright.Dispose();
}
}
قدم ۵: نوشتن تست لاگین
فایل LoginTests.cs:
using Microsoft.Playwright;
using static Microsoft.Playwright.Assertions;
namespace PlaywrightDemo.Tests;
public class LoginTests : IClassFixture<PlaywrightFixture>
{
private const string BaseUrl = "http://localhost:5000";
private readonly PlaywrightFixture _fixture;
public LoginTests(PlaywrightFixture fixture)
{
_fixture = fixture;
}
[Fact]
public async Task Should_Show_Welcome_After_Login()
{
// ساخت Context جداگانه برای تست
await using var context = await _fixture.Browser.NewContextAsync();
var page = await context.NewPageAsync();
// رفتن به صفحه لاگین
await page.GotoAsync($"{BaseUrl}/Account/Login");
// پر کردن فرم
await page.GetByLabel("Username").FillAsync("admin");
await page.GetByLabel("Password").FillAsync("123456");
// کلیک روی دکمه Login
await page.GetByRole(AriaRole.Button, new() { Name = "Login" }).ClickAsync();
// انتظار برای نمایش پیام خوشآمد
var heading = page.GetByRole(AriaRole.Heading, new() { Name = "خوش آمدید، admin!" });
await Expect(heading).ToBeVisibleAsync();
}
}
قدم ۶: اجرای برنامه و تست
ابتدا برنامه وب را روی پورت 5000 اجرا کن:
cd PlaywrightDemo.Web
dotnet run --urls=http://localhost:5000
در ترمینال دیگر تستها را اجرا کن:
cd PlaywrightDemo.Tests
dotnet test
۷. مباحث پیشرفته
۷.۱ ایزولهسازی با Context برای تستهای موازی
بهجای بهاشتراکگذاشتن یک IPage بین تستها، برای هر تست یک IBrowserContext جدید بساز. این کار کوکیها و Sessionها را ایزوله نگه میدارد:
[Fact]
public async Task Test_With_Isolated_Context()
{
await using var context = await _fixture.Browser.NewContextAsync();
var page = await context.NewPageAsync();
// تست بدون تداخل با تستهای دیگر
}
۷.۲ ضبط Trace
Trace برای دیباگ خطاها در CI بسیار مفید است:
await context.Tracing.StartAsync(new TracingStartOptions
{
Screenshots = true,
Snapshots = true
});
try
{
// ... عملیات تست ...
}
finally
{
await context.Tracing.StopAsync(new TracingStopOptions
{
Path = "trace.zip"
});
}
برای مشاهده Trace:
pwsh bin/Debug/net8.0/playwright.ps1 show-trace trace.zip
۷.۳ ضبط ویدیو
var context = await browser.NewContextAsync(new BrowserNewContextOptions
{
RecordVideoDir = "videos",
ViewportSize = new ViewportSize { Width = 1280, Height = 720 }
});
var page = await context.NewPageAsync();
await page.GotoAsync("http://localhost:5000");
// بعد از پایان تست، ویدیو در پوشه videos ذخیره میشود
await context.DisposeAsync();
۷.۴ شبیهسازی API با Route
میتوانی درخواستهای شبکه را قطع کنی و پاسخ جعلی برگردانی:
await page.RouteAsync("**/api/user", async route =>
{
await route.FulfillAsync(new RouteFulfillOptions
{
Status = 200,
ContentType = "application/json",
Body = "{\"name\":\"admin\",\"role\":\"admin\"}"
});
});
await page.GotoAsync("http://localhost:5000/users");
یا درخواستهای اضافی مثل تصاویر را مسدود کن:
await page.RouteAsync("**/*.{png,jpg,jpeg}", route => route.AbortAsync());
۷.۵ ذخیره و بازیابی State احراز هویت
بعد از لاگین، میتوانی Session را ذخیره و در تستهای بعدی استفاده کنی:
// ذخیره state بعد از لاگین
await context.StorageStateAsync(new StorageStateOptions
{
Path = "state.json"
});
// استفاده در Context جدید
var context = await browser.NewContextAsync(new BrowserNewContextOptions
{
StorageStatePath = "state.json"
});
۷.۶ استفاده از data-testid
در Viewهای Razor خود از data-testid استفاده کن تا Locatorها پایدارتر شوند:
<button type="submit" data-testid="login-button">Login</button>
سپس در تست:
await page.GetByTestId("login-button").ClickAsync();
۸. اشتباهات رایج
نصب نکردن مرورگرها
بعد ازdotnet add package Microsoft.Playwrightفکر میکنی همهچیز آماده است. اما باید حتماًplaywright.ps1 installرا اجرا کنی.باز گذاشتن Browser و Context
اگر Browser یا Playwright را Dispose نکنی، مرورگرها باز میمانند و منابع سیستم هدر میرود. همیشه ازawait usingیاDisposeAsyncاستفاده کن.استفاده از Selectorهای CSS شکننده
Selectorهایی مثلdiv > spanبه محض تغییر ساختار HTML میشکنند. بهجای آن ازGetByRole،GetByLabelیاGetByTestIdاستفاده کن.استفاده از
Thread.SleepیاWaitForTimeout
Playwright انتظار خودکار دارد. استفاده از Sleep باعث کندی و تستهای غیرقابل اعتماد میشود. از Assertionها و انتظارهای صریح استفاده کن.اتصال به TestServer بهجای Kestrel واقعی
اگر ازWebApplicationFactoryاستفاده میکنی، دقت کن کهTestServerدرونحافظه است و مرورگر نمیتواند به آن متصل شود. برای تست با Playwright باید برنامه را روی یک پورت واقعی اجرا کنی.فراموش کردن اجرای برنامه قبل از تست
تستهای Playwright به یک URL واقعی نیاز دارند. قبل ازdotnet test، برنامه ASP.NET Core را باdotnet runاجرا کن یا در CI یک سرویس واقعی بالا بیاور.
۹. بهترین روشها (Best Practices)
از Locatorهای مدرن استفاده کن
GetByRole،GetByLabel،GetByTestIdرا به Selectorهای CSS ترجیح بده.هر تست یک Context جداگانه داشته باشد
این کار تستهای موازی را ایزوله و قابل اعتماد نگه میدارد.از انتظار خودکار استفاده کن
بهجایWaitForTimeoutازExpect(locator).ToBeVisibleAsync()استفاده کن.از
data-testidدر Viewها استفاده کن
این ویژگی انتخابها را از جزئیات ظاهری جدا میکند.در CI از حالت Headless استفاده کن
مرورگر بدون رابط کاربری سریعتر و پایدارتر است.خطاها را با Trace ضبط کن
در CI همیشه Trace را ذخیره کن تا دیباگ خطاها راحتتر شود.تستها را مستقل از یکدیگر بنویس
هیچ تستی نباید به ترتیب یا state تست دیگر وابسته باشد.از ذخیره State برای تستهای نیازمند لاگین استفاده کن
بهجای لاگین در ابتدای هر تست، State را یک بار ذخیره و بارگذاری کن.URL پایه را ثابت نگه دار
یک ثابتBaseUrlتعریف کن و در همه تستها از آن استفاده کن.
۱۰. منابع و ادامه مسیر
- مستندات رسمی Playwright برای .NET
- مخزن GitHub رسمی Playwright
- آموزش تست برنامههای ASP.NET Core
- دورههای Microsoft Learn درباره تستنویسی
- کانال Discord رسمی Playwright
مسیر پیشنهادی بعد از این آموزش
- یک پروژه Blazor Server بساز و تستهای End-to-End برای آن بنویس.
- در CI/CD خود Playwright را با GitHub Actions یا Azure DevOps ادغام کن.
- تستهای چندمرورگری (Chromium + Firefox + WebKit) را اجرا کن.
- با Network Mocking و API Mocking سناریوهای پیچیده را شبیهسازی کن.
- از Playwright Codegen برای تولید خودکار Locator و کدهای تست استفاده کن:
pwsh bin/Debug/net8.0/playwright.ps1 codegen http://localhost:5000