Playwright has quickly become one of the most powerful and reliable tools for end-to-end web testing and browser automation. Developed by Microsoft, it lets you automate Chromium, Firefox, and WebKit with a single API.
If you are a C# developer used to older tools like Selenium, you'll find Playwright to be faster, more reliable, and far easier to set up thanks to its built-in auto-waiting and modern async/await patterns.
Why Playwright Beats Traditional Automation
Before diving into the code, let's look at why developers love Playwright:
Auto-Waiting: Playwright automatically waits for elements to be actionable (visible, enabled, stable) before performing clicks or type actions. No more scattered
Thread.Sleep()or flaky explicit waits.Built-in Test Runners & Isolation: Each test runs in an isolated browser context (like a brand new incognito window), ensuring tests don't bleed state into one another.
Multi-Browser Support: Test across Chromium, Firefox, and Safari (WebKit) effortlessly.
Codegen & Trace Viewer: Playwright can record your browser actions and generate C# code for you, and its Trace Viewer lets you step backward and forward through your test runs visually.
Setting Up Playwright in C#
Getting started takes just a few terminal commands. You can use a standard NUnit or MSTest project. Here's how to set up a new project:
Create a new NUnit test project:
Bash
dotnet new nunit -n PlaywrightDemo cd PlaywrightDemoInstall the Playwright NuGet package:
Bash
dotnet add package Microsoft.Playwright.NUnitInstall the browser binaries:
Bash
pwsh bin/Debug/netX.X/playwright.ps1 install(Or simply run
dotnet buildand execute the generated installation script).
Your First Playwright C# Test
Let's write a clean, readable test that navigates to a sample website, performs a search, and asserts that the results appear correctly.
C#
using Microsoft.Playwright;
using Microsoft.Playwright.NUnit;
using NUnit.Framework;
namespace PlaywrightDemo;
[TestFixture]
public class SearchTests : PageTest
{
[Test]
public async Task SearchGitHubForPlaywright()
{
// 1. Navigate to the target website
await Page.GotoAsync("https://github.com");
// 2. Locate the search input and type our query
// Playwright's locator engine makes finding elements intuitive
await Page.Locator("[placeholder='Search GitHub']").ClickAsync();
await Page.Keyboard.TypeAsync("microsoft/playwright-dotnet");
await Page.Keyboard.PressAsync("Enter");
// 3. Wait for navigation and verify search results
await Page.WaitForURLAsync("**/search?q=microsoft%2Fplaywright-dotnet*");
// 4. Assert that the correct repository appears in the results
var firstResult = Page.Locator("a[href='/microsoft/playwright-dotnet']").First;
await Expect(firstResult).ToBeVisibleAsync();
}
}
Key Concepts Explained
1. The PageTest Base Class
When using Microsoft.Playwright.NUnit, inheriting from PageTest automatically provisions a fresh browser instance, context, and Page object for every single test method. You don't have to worry about manual setup or teardown boilerplate.
2. Powerful Locators
Playwright encourages user-facing locators (like text, roles, and placeholders) rather than fragile CSS classes or complex XPaths.
By Role:
Page.GetByRole(AriaRole.Button, new() { Name = "Sign in" })By Text:
Page.GetByText("Welcome back")By Placeholder:
Page.GetByPlaceholder("Enter your email")
3. Built-In Assertions (Expect)
Playwright provides robust web-first assertions that automatically retry until the expected condition is met (up to a timeout):
C#
await Expect(Page.GetByText("Success")).ToBeVisibleAsync();
Summary Table: Playwright vs. Selenium
Feature | Selenium | Playwright (.NET) |
Wait Strategy | Manual explicit/implicit waits | Automatic waiting for element stability |
Network Interception | Complex setup | Built-in mocking and routing |
Architecture | WebDriver protocol over HTTP | Direct WebSocket connection to browser binaries |
Multi-tab/Context | Difficult to manage cleanly | Native browser contexts for true isolation |