使用 Playwright 测试 & . NET 的多Q- @ browser E2_E 浏览器
Playwright 是用于现代浏览器自动化的 微软 @ ' @ s官方解决方案@ MS K1} 提供多 - browser 支持 *(Chrome @ MPK4Firefox,Safari}) 使用一个单一 API}MS K7 正在建造\ mskK8 在追踪调试@, 和移动模擬. 本指南覆盖了从设置到高级测试模式的所有内容 {,_ 比较它与普开胃人Sharp 来帮助您选择正确的工具>% .} 如果您只需要 Chrome 并且想要更简单的设置\ MS k13} 夏尔普 @ -} @ 但是如果您需要综合的 cross@ MS K1_ browser 测试@ I, Playwright 是前进的路 # .#
一. 导言导言 导言 导言 导言
如果你读了我的文章 夏尔普'Q}♪PutopteeerSharp是Chrome@-只測試,,但若你需要通過多個瀏覽器做檢測...? 那又如何呢? lay人赖 进来了 .
Playwright 是 微软 @ MSKK0 @ sans 回答浏览器自动化问题@ MS K1 @ 并且他们已经从之前的每件事中学到了' @ it'}s like Putchapeer Sharp{'}它们更雄心勃勃的sibling @ I-}它做了所有的玩偶都做 ,}但是增加了 Firefox和Safari 支持@,}更好的自动=-# 等待+,}以及一系列调试工具,让发现的问题成为绝对dodleQ.}
在文章“,” I'中,将展示您如何使用 Playwright 来测试 Chrome @.NET 和Safari@, 和 Safari},。 您今天可以使用真实的代码示例和实际模式_.}
为什么《玩偶剧作家》Sharp?
不要误会我 夏尔普 如果您只需要 Chrome@ . @ 但是这里 @ MS K1} 当玩家游戏机说得通时 @ MPK2 @
多@ - @%Browser 问题
您的使用者使用chrome@.}他们使用 @ :
- 铬/@(chromium-}基于@)
- 萨法里@(# 特别是移动@)}
- 消防@(}但通常不同发音#)
唯有在 Safari 中出现的错误才会失去您的潜在用户@20% @% Playwright 让您用相同的 API#.来测试所有三者 #
更好的开发者经验
graph TB
subgraph "PuppeteerSharp"
PS[Chrome Only]
PS --> PS1[Fast setup]
PS --> PS2[Simple API]
PS --> PS3[Chrome DevTools]
end
subgraph "Playwright"
PW[Multi-Browser]
PW --> PW1[Chrome, Firefox, Safari]
PW --> PW2[Auto-waiting built-in]
PW --> PW3[Trace viewer]
PW --> PW4[Codegen tool]
PW --> PW5[Better error messages]
end
style PW stroke:#00aa00,stroke-width:3px
style PW1 stroke:#0066cc,stroke-width:2px
style PW3 stroke:#0066cc,stroke-width:2px
style PW4 stroke:#0066cc,stroke-width:2px
关键优点
- 支持多@ -_browser 支持 -_Test Chrome,_Firefox},_Safari 使用相同的代码
- 更好的自动@- @ 等待 更可靠地从盒子里出来 ,
- 追踪查看器 - 记录用于调试的测试运行的全部痕迹
- 腐素 @-_ 通过与您的网站互动生成测试代码
- 网络拦截 ~ - ~ 比傀儡更强大 ~
- 现代网络功能 更好地支持阴影 DOM @, @iframes@,}等#.
为 @ .@NET 设置 Playwright
安装 微軟.Playwright NuGet 软件包@: @% 1
dotnet add package Microsoft.Playwright
dotnet add package Microsoft.Playwright.NUnit # Or use xUnit
然后安装浏览器@ ( @% 此下载 铬, 消防,和 WebKit 网络文件):
pwsh bin/Debug/net9.0/playwright.ps1 install
或Linux@/ @Mac_:
playwright install
注:: 首个浏览器安装下载到 @400MB.}后继更新则小得多@.
这里 @ ' @\ title: profile
<PackageReference Include="Microsoft.Playwright" Version="1.48.0" />
<PackageReference Include="Microsoft.Playwright.NUnit" Version="1.48.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
I'% 使用 x 单位,,但剧作家工作同样不错 N单位 或 微测试“.”事实上,“,” 剧作家有一个专注的作品 N单位一体化 附加助手@. @%
创建基准测试类
类似 夏尔普,, 但有多@ MS K1browser 支持 *:
阶级结构
using Microsoft.Playwright;
using Xunit.Abstractions;
namespace Mostlylucid.Test.E2E;
public abstract class PlaywrightTestBase : IAsyncLifetime
{
protected IPlaywright Playwright = null!;
protected IBrowser Browser = null!;
protected IBrowserContext Context = null!;
protected IPage Page = null!;
protected readonly ITestOutputHelper Output;
protected const string BaseUrl = "http://localhost:8080";
// Override this in derived classes to test different browsers
protected virtual BrowserType BrowserType => BrowserType.Chromium;
protected PlaywrightTestBase(ITestOutputHelper output)
{
Output = output;
}
public async Task InitializeAsync()
{
// Create Playwright instance
Playwright = await Microsoft.Playwright.Playwright.CreateAsync();
// Launch the specified browser
Browser = await LaunchBrowserAsync();
// Create a new context (like an incognito window)
Context = await Browser.NewContextAsync(new BrowserNewContextOptions
{
ViewportSize = new ViewportSize { Width = 1400, Height = 900 },
IgnoreHTTPSErrors = true
});
// Create a new page
Page = await Context.NewPageAsync();
// Set default timeout
Page.SetDefaultTimeout(30000);
}
private async Task<IBrowser> LaunchBrowserAsync()
{
var options = new BrowserTypeLaunchOptions
{
Headless = true, // Set to false for debugging
};
return BrowserType switch
{
BrowserType.Chromium => await Playwright.Chromium.LaunchAsync(options),
BrowserType.Firefox => await Playwright.Firefox.LaunchAsync(options),
BrowserType.Webkit => await Playwright.Webkit.LaunchAsync(options),
_ => throw new ArgumentException($"Unknown browser type: {BrowserType}")
};
}
public async Task DisposeAsync()
{
await Page?.CloseAsync()!;
await Context?.CloseAsync()!;
await Browser?.CloseAsync()!;
Playwright?.Dispose();
}
}
public enum BrowserType
{
Chromium,
Firefox,
Webkit
}
为什么浏览器上下文@ ? @
我们创造一个 Context 在创建 Page@ . @ 这是玩耍者的概念, Putppiteer Sharp doesn't makingK1 t\ :}
- 背景背景 @ =_ 孤立的浏览器会话@ (}\ 类似无意识模式 @ MPK2
- 页次 页次 页次 页 页次 页次 =在此背景下的标签
上下文允许您@: @%
- 同时测试不同的用户会话
- 每个上下文设置不同的 cookies@ , @ 权限 @ MS K1}% 或位置
- 孤立测试完全@( @% 即使在同一浏览器实例中 @)}
帮助方法
// Navigation with better waiting
protected async Task NavigateAsync(string path)
{
var url = path.StartsWith("http") ? path : $"{BaseUrl}{path}";
await Page.GotoAsync(url, new PageGotoOptions
{
WaitUntil = WaitUntilState.NetworkIdle
});
}
// Playwright has better built-in waiting, but these are still useful
protected async Task<IElementHandle?> WaitForSelectorAsync(string selector)
{
try
{
return await Page.WaitForSelectorAsync(selector, new PageWaitForSelectorOptions
{
State = WaitForSelectorState.Visible
});
}
catch (TimeoutException)
{
return null;
}
}
// Improved element operations with auto-waiting
protected async Task<bool> ElementExistsAsync(string selector)
{
return await Page.Locator(selector).CountAsync() > 0;
}
protected async Task<string?> GetTextContentAsync(string selector)
{
var locator = Page.Locator(selector);
return await locator.TextContentAsync();
}
protected async Task TypeAsync(string selector, string text)
{
await Page.Locator(selector).FillAsync(text);
}
protected async Task ClickAsync(string selector)
{
await Page.Locator(selector).ClickAsync();
}
定位器 @ - @ 游戏机@ MS K1%s 秘密武器
通知 Page.Locator() 与 Putppeeer Sharp@'+%s 不同 QuerySelector“.”定位器是“:”
- 懒惰 @-他们不问DOM 直到你使用它们
- 重试 - 它们自动等待和重试操作
- 严格 - 多个元素匹配 {( 防止弹片测试)} 它们出错 。
// PuppeteerSharp way (manual waiting)
await Page.WaitForSelectorAsync("#button");
await Page.ClickAsync("#button");
// Playwright way (auto-waiting)
await Page.Locator("#button").ClickAsync(); // Waits automatically!
多@ - @%Browser 测试
您可以在所有浏览器中进行相同的测试@ : @
使用 x Unit 理论进行多@ - @Browser 测试
public class CrossBrowserTests : PlaywrightTestBase
{
public CrossBrowserTests(ITestOutputHelper output) : base(output) { }
[Theory]
[InlineData(BrowserType.Chromium)]
[InlineData(BrowserType.Firefox)]
[InlineData(BrowserType.Webkit)]
public async Task HomePage_LoadsCorrectly_InAllBrowsers(BrowserType browserType)
{
// Override the browser type for this test
BrowserType = browserType;
await InitializeAsync();
// Arrange & Act
await NavigateAsync("/");
// Assert
var title = await Page.TitleAsync();
Assert.Contains("Mostlylucid", title);
Output.WriteLine($"✓ Test passed in {browserType}");
}
}
浏览器@ - @ 特定测试类
或为每个浏览器创建单独的测试类@: @%
public class ChromiumTests : PlaywrightTestBase
{
protected override BrowserType BrowserType => BrowserType.Chromium;
public ChromiumTests(ITestOutputHelper output) : base(output) { }
[Fact]
public async Task FilterBar_WorksInChrome()
{
await NavigateAsync("/blog");
await Page.Locator("#LanguageDropDown button").ClickAsync();
await Page.Locator("#LanguageDropDown li:has-text('Spanish')").ClickAsync();
var url = Page.Url;
Assert.Contains("language=es", url);
}
}
public class FirefoxTests : PlaywrightTestBase
{
protected override BrowserType BrowserType => BrowserType.Firefox;
public FirefoxTests(ITestOutputHelper output) : base(output) { }
[Fact]
public async Task FilterBar_WorksInFirefox()
{
// Same test, different browser!
await NavigateAsync("/blog");
await Page.Locator("#LanguageDropDown button").ClickAsync();
await Page.Locator("#LanguageDropDown li:has-text('Spanish')").ClickAsync();
var url = Page.Url;
Assert.Contains("language=es", url);
}
}
public class SafariTests : PlaywrightTestBase
{
protected override BrowserType BrowserType => BrowserType.Webkit;
public SafariTests(ITestOutputHelper output) : base(output) { }
[Fact]
public async Task FilterBar_WorksInSafari()
{
await NavigateAsync("/blog");
await Page.Locator("#LanguageDropDown button").ClickAsync();
await Page.Locator("#LanguageDropDown li:has-text('Spanish')").ClickAsync();
var url = Page.Url;
Assert.Contains("language=es", url);
}
}
写作测试 *\ - 玩人之路
让我们来看看显示 Playwright ' 优势的真实例子
使用 Auto@- @ 等待测试 HTMX
[Fact]
public async Task SortingDropdown_UpdatesContentViaHTMX()
{
// Arrange
await NavigateAsync("/blog");
// Get first post title before sorting
var firstPostBefore = await Page.Locator("article h2 a").First.TextContentAsync();
Output.WriteLine($"First post before: {firstPostBefore}");
// Act - Change sort order
await Page.Locator("#orderSelect").SelectOptionAsync("date_asc");
// Playwright automatically waits for HTMX to update the DOM!
await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
// Assert - Content should have changed
var firstPostAfter = await Page.Locator("article h2 a").First.TextContentAsync();
Output.WriteLine($"First post after: {firstPostAfter}");
var selectValue = await Page.Locator("#orderSelect").InputValueAsync();
Assert.Equal("date_asc", selectValue);
}
测试 Alpine.js 互动
Playwright 手柄 阿尔卑斯山 活动与动画无缝无缝@: @%
[Fact]
public async Task AlpineDropdown_OpensAndCloses()
{
await NavigateAsync("/blog");
// Click to open dropdown (Alpine.js controlled)
await Page.Locator("#LanguageDropDown button").ClickAsync();
// Wait for Alpine.js animation
await Page.WaitForSelectorAsync("#LanguageDropDown div[x-show]", new()
{
State = WaitForSelectorState.Visible
});
// Check dropdown is visible
var isVisible = await Page.Locator("#LanguageDropDown div[x-show]").IsVisibleAsync();
Assert.True(isVisible);
// Click outside to close
await Page.Locator("body").ClickAsync(new LocatorClickOptions
{
Position = new Position { X = 0, Y = 0 }
});
// Verify closed
await Page.WaitForSelectorAsync("#LanguageDropDown div[x-show]", new()
{
State = WaitForSelectorState.Hidden
});
isVisible = await Page.Locator("#LanguageDropDown div[x-show]").IsVisibleAsync();
Assert.False(isVisible);
}
带有校验的测试表格
[Fact]
public async Task CommentForm_ValidatesRequiredFields()
{
await NavigateAsync("/blog/some-post");
// Try to submit without filling fields
await Page.Locator("#comment-submit").ClickAsync();
// Check HTML5 validation messages
var nameInput = Page.Locator("#comment-name");
var validationMessage = await nameInput.EvaluateAsync<string>("el => el.validationMessage");
Assert.NotEmpty(validationMessage);
Output.WriteLine($"Validation message: {validationMessage}");
// Fill form properly
await nameInput.FillAsync("Test User");
await Page.Locator("#comment-email").FillAsync("[email protected]");
await Page.Locator("#comment-content").FillAsync("Great article!");
// Submit
await Page.Locator("#comment-submit").ClickAsync();
// Wait for success
await Page.Locator(".comment-success").WaitForAsync();
}
高级 Playwright 特征
网络截取@ - 比木偶更强大
[Fact]
public async Task PageLoadsWithoutImages_ToTestPerformance()
{
// Block all image requests
await Page.RouteAsync("**/*.{png,jpg,jpeg,gif,webp}", route => route.AbortAsync());
await NavigateAsync("/blog");
// Page should still function without images
var title = await Page.Locator("h1").TextContentAsync();
Assert.NotEmpty(title);
}
[Fact]
public async Task MockAPIResponse_ForTesting()
{
// Intercept API calls and return mock data
await Page.RouteAsync("**/api/posts", route => route.FulfillAsync(new()
{
Status = 200,
ContentType = "application/json",
Body = System.Text.Json.JsonSerializer.Serialize(new
{
posts = new[]
{
new { id = 1, title = "Mock Post 1" },
new { id = 2, title = "Mock Post 2" }
}
})
}));
await NavigateAsync("/blog");
// Should show mock data
var posts = await Page.Locator(".post-title").CountAsync();
Assert.Equal(2, posts);
}
追踪查看器 @ -+% 调试失败测试
public async Task InitializeAsync()
{
Playwright = await Microsoft.Playwright.Playwright.CreateAsync();
Browser = await LaunchBrowserAsync();
// Start tracing
Context = await Browser.NewContextAsync();
await Context.Tracing.StartAsync(new()
{
Screenshots = true,
Snapshots = true,
Sources = true
});
Page = await Context.NewPageAsync();
}
public async Task DisposeAsync()
{
// Save trace on test completion
var tracePath = Path.Combine("traces", $"{TestContext.TestName}.zip");
await Context.Tracing.StopAsync(new()
{
Path = tracePath
});
// View trace with: playwright show-trace traces/TestName.zip
await Page?.CloseAsync()!;
await Context?.CloseAsync()!;
await Browser?.CloseAsync()!;
Playwright?.Dispose();
}
然后查看跟踪@ : @
playwright show-trace traces/TestName.zip
这将打开一个显示\ :\ 的 UI 。
- 你每做一次测试
- 每一步的截图
- 联网请求
- 控制台日志
- DOM快照
{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}非常聪明的调试...
失败的截图
public async Task DisposeAsync()
{
// Take screenshot if test failed
if (TestContext.CurrentTestOutcome != TestOutcome.Passed)
{
var screenshot = await Page.ScreenshotAsync();
File.WriteAllBytes($"failure-{TestContext.TestName}.png", screenshot);
Output.WriteLine($"Screenshot saved: failure-{TestContext.TestName}.png");
}
await Page?.CloseAsync()!;
await Context?.CloseAsync()!;
await Browser?.CloseAsync()!;
Playwright?.Dispose();
}
移动模拟
[Fact]
public async Task BlogPage_WorksOnMobile()
{
// Create context with mobile emulation
var iPhone = Playwright.Devices["iPhone 13"];
await using var context = await Browser.NewContextAsync(iPhone);
await using var page = await context.NewPageAsync();
await page.GotoAsync($"{BaseUrl}/blog");
// Check mobile menu is visible
var mobileMenu = page.Locator(".mobile-menu");
await Expect(mobileMenu).ToBeVisibleAsync();
// Desktop menu should be hidden
var desktopMenu = page.Locator(".desktop-menu");
await Expect(desktopMenu).Not.ToBeVisibleAsync();
}
游戏剧作家随来 设备描述器 {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}不,不!
- {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}
- iPad,iPad pro
- Samsung 银河系统Name,像素
- 还有更多
测试黑暗模式
[Fact]
public async Task DarkMode_TogglesCorrectly()
{
// Start with dark color scheme
await using var context = await Browser.NewContextAsync(new()
{
ColorScheme = ColorScheme.Dark
});
await using var page = await context.NewPageAsync();
await page.GotoAsync($"{BaseUrl}");
// Check dark mode is active
var isDark = await page.EvaluateAsync<bool>(
"() => window.matchMedia('(prefers-color-scheme: dark)').matches"
);
Assert.True(isDark);
// Check background color reflects dark mode
var bgColor = await page.Locator("body").EvaluateAsync<string>(
"el => getComputedStyle(el).backgroundColor"
);
Assert.Contains("rgb(0, 0, 0)", bgColor); // Dark background
}
Playwright 和 Putppeteer Sharp 并排
这里的'+% 在两个图书馆都用相同的测试来显示差异@ :_ @
Putpeeer 开胃乐版本
[Fact]
public async Task FilterTest_PuppeteerSharp()
{
await Page.GoToAsync("http://localhost:8080/blog");
// Manual waiting required
await Page.WaitForSelectorAsync("#LanguageDropDown button");
await Page.ClickAsync("#LanguageDropDown button");
// Wait for dropdown animation
await Task.Delay(300);
// Click Spanish option
await Page.WaitForSelectorAsync("#LanguageDropDown li:nth-child(2) a");
await Page.ClickAsync("#LanguageDropDown li:nth-child(2) a");
// Wait for navigation
await Task.Delay(500);
// Check URL
var url = Page.Url;
Assert.Contains("language=", url);
}
Playwright 版本
[Fact]
public async Task FilterTest_Playwright()
{
await Page.GotoAsync("http://localhost:8080/blog");
// Auto-waiting built in
await Page.Locator("#LanguageDropDown button").ClickAsync();
// Click Spanish option (waits automatically for visibility)
await Page.Locator("#LanguageDropDown li:has-text('Spanish')").ClickAsync();
// Check URL (waits automatically for navigation)
await Expect(Page).ToHaveURLAsync(new Regex(".*language=.*"));
}
通知@:
- 无手动手册
WaitForSelectorAsync所需 - 否 无
Task.Delay所需 - 更清洁的主张
Expect - 更易读用
has-text选择器
PDF 使用普laywright 的 PLaw 生成器
类似 PutchapeerSharp,Playwright 可以生成 PDFs @.。 API几乎相同@ :
public async Task<byte[]> GeneratePdfAsync(string url)
{
await using var browser = await Playwright.Chromium.LaunchAsync();
await using var page = await browser.NewPageAsync();
await page.GotoAsync(url);
return await page.PdfAsync(new()
{
Format = "A4",
PrintBackground = true,
Margin = new()
{
Top = "20mm",
Right = "20mm",
Bottom = "20mm",
Left = "20mm"
}
});
}
相同 PDF 从 PutoppeeerSharp 文章中获取CHA 在此应用@ . @%
何时选择 Playwright
graph TD
A[Need E2E Testing?] --> B{Multi-browser required?}
B -->|Yes| C[Playwright]
B -->|No| D{Chrome only?}
D -->|Yes| E[PuppeteerSharp or Playwright]
D -->|No| C
C --> F[Benefits]
F --> F1[Test all major browsers]
F --> F2[Better debugging tools]
F --> F3[More reliable waiting]
F --> F4[Mobile emulation]
E --> G[PuppeteerSharp Benefits]
G --> G1[Simpler setup]
G --> G2[Smaller overhead]
G --> G3[Chromium focused]
style C stroke:#00aa00,stroke-width:3px
style E stroke:#0066cc,stroke-width:2px
:\ \ 时选择 Playwright
- 您需要多@ - @ 浏览器测试
- 您想要更好的调试工具@ ( @ ttrace 查看器 @ MS K1 @
- 您正在测试复杂的现代网络应用程序
- 你需要移动模拟
- 你想从盒子里得到更可靠的测试
选择开胃菜沙尔普时@ : @
- 只有Chrome / 仅铬才算罚款
- 你想要更简单的设置
- 您已经熟悉木偶剧了
- 您需要略少一点资源管理费
在 CI/}CD 中运行
GitHub 行动实例
Playwright 在 CI/CD 管道@ . 这里 @ MS K2 完整 吉特胡布行动组织 工作流程@: @%
name: Playwright Tests
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: '9.0.x'
- name: Install dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Install Playwright browsers
run: pwsh Mostlylucid.Test/bin/Debug/net9.0/playwright.ps1 install --with-deps
- name: Start application
run: |
dotnet run --project Mostlylucid/Mostlylucid.csproj &
echo $! > app.pid
- name: Wait for application
run: |
timeout 60 bash -c 'until curl -f http://localhost:8080/health; do sleep 2; done'
- name: Run Playwright tests
run: |
dotnet test Mostlylucid.Test/Mostlylucid.Test.csproj \
--filter "Category=E2E" \
--logger "console;verbosity=detailed"
- name: Upload test results
if: always()
uses: actions/upload-artifact@v3
with:
name: playwright-results
path: |
**/test-results/
**/traces/
- name: Stop application
if: always()
run: kill $(cat app.pid) || true
嵌套支持
正在运行 Playwright 在 嵌入器 需要安装系统依赖性 *:
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
# Install Playwright dependencies
RUN apt-get update && apt-get install -y \
libnss3 \
libnspr4 \
libatk1.0-0 \
libatk-bridge2.0-0 \
libcups2 \
libdrm2 \
libxkbcommon0 \
libxcomposite1 \
libxdamage1 \
libxfixes3 \
libxrandr2 \
libgbm1 \
libasound2
WORKDIR /app
COPY . .
RUN dotnet restore
RUN dotnet build
RUN pwsh Mostlylucid.Test/bin/Debug/net9.0/playwright.ps1 install
CMD ["dotnet", "test"]
业绩比较
从我在这个博客的测试中,
| @ | @ 地物 @ I | @ PutppeteerSharp @ MPK2_ 播放器@ MS K3# | |
|---|---|---|---|
| 铬测试速度 @ | @ @ ~2.5 @ @ * | @ * MS K3 @ * | # |
| 多@ -_browser | + @❌+ @ | + ✓+#+Adds #每个浏览器的~1+Z#MS K6} @ | # |
| 等待可靠性 (自动) | |||
| 每个浏览器的内存 -=YTET -伊甸园字幕组=- 翻译: | |||
| 设置复杂配置 “ | 简单”“ | ”中度“MS K2” | |
| 调试工具 | 铬DevTools | 踪迹檢視器MS K2 DevTools | |
| 移动模拟 -=YTET -伊甸园字幕组=- 翻译: |
Playwright 稍慢一点,使用稍微多一些内存@, @}但可靠性和调试效果通常大于此@ . @%
共犯组织
严格模式
Playwright'%s 定位器默认会严格@:
// This errors if multiple buttons exist
await Page.Locator("button").ClickAsync();
// Be specific
await Page.Locator("button#submit").ClickAsync();
// Or use .First if you really want the first match
await Page.Locator("button").First.ClickAsync();
2. 浏览器安装
别忘了跑 playwright install 在添加软件包@ . 后, 浏览器是 @ MS K1}% 1\ t 包含在 Nuget 软件包中@ MPK2\ {
3. 上下文與頁面
记住上下文分离测试@: @%
// Bad - state leaks between tests
await Page.GotoAsync("/login");
// Do stuff...
await Page.GotoAsync("/dashboard");
// Previous login state might still exist
// Good - fresh context per test
await using var context = await Browser.NewContextAsync();
await using var page = await context.NewPageAsync();
// Completely isolated
4.+Webkit差异
Safari @( @WebKit@)# 行为可能不同 @ : @
// May work in Chrome but fail in WebKit
await Page.Locator(".dropdown").Hover();
await Page.Locator(".dropdown-item").ClickAsync();
// More reliable across browsers
await Page.Locator(".dropdown").ClickAsync();
await Page.WaitForSelectorAsync(".dropdown-item");
await Page.Locator(".dropdown-item").ClickAsync();
结论结论
Playwright 代表了 & M. @% NET 开发者的浏览器自动化的进化@ . @ @ k1 @ 如果您需要多 @ MS K2 browser 支持 < ,} it'{ 夏尔普但福利相当可观,
- 在所有主要浏览器中以相同的代码测试
- 自动等待的可靠性提高
- 使用跟踪查看器进行极佳的调试
- 盒子外的移动和平板仿冒
- 更能应对时间安排问题
我的建议#:
- 需要交叉@ -_ 浏览器测试的新工程:BARBAR 以剧作家開始
- Chrome - @ 仅项目@:_PuppeteerSharp 更简单一些
- 现有木偶人项目: 只需要多@-browser 才能移動
- 复杂的现代网络应用程序:Playwright'工具值间接费用。
仅此追踪查看器就让我节省了调试@. @ 能够重放一个失败的测试, 并看到浏览器每一步所看到的准确性是宝贵的 @ .} @
让 Playwright 开始您的下一个工程@ - @ you' @ 会很惊讶它能让 CrossZ-_ browser 测试Q.#