Back to "Multi---Browser-Test mit Playwright für .-NET"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

E2E Testing Multi-Browser Playwright Testing xUnit

Multi---Browser-Test mit Playwright für .-NET

Thursday, 27 November 2025

Playwright ist Microsoft®'s offizielle Lösung für die moderne Browser-Automatisierung., , bietet Multi-- , Browser-Unterstützung , ( , Chrome und mobile Emulation ,. Diese Anleitung umfasst alles von der Einrichtung bis zu fortgeschrittenen Testmustern , die es mit PuppeteerSharp vergleichen, um Ihnen zu helfen, das richtige Tool wählen , . , Wenn Sie nur benötigen Chrome und wollen einfachere Setup- -, PuppeteerSharp Stattdessen -, aber wenn Sie umfassende cross-Tests benötigen -Browser testing,... Playwright ist der Weg vorwärts

Einleitung

Wenn Sie lesen meinen Artikel auf PuppeteerSharp' ist ein Fan von modernen Test-Tools, die don.'t machen Sie wollen Ihre Haare ausreißen ? MS K5 puppeteerSharp ist brillant für Chrome ?- nur Tests ?, , aber was, wenn Sie über mehrere Browser testen müssen ? Wiedergaberecht kommt in.

Playwright ist eine Antwort von Microsoft auf das Problem der Browser-Automatisierung, , und sie ' haben aus allem gelernt, was zuvor gekommen ist... Es kommt wie PuppeteerSharp's ambitionierteres Geschwisterchen. -. Es macht alles, was Puppeteer tut, aber Firefox und Safari Unterstützung fügt hinzu: ,. Bessere auto.-.Warten mit ,. Und ein Haufen Debugging-Tools, die das Finden von Problemen zu einem absoluten Doddle machen.

In diesem Artikel zeigen Ihnen, wie man Playwright für Chrome-,., und Safari mit echten Codebeispielen und praktischen Mustern, die Sie heute verwenden können, zu testen

Warum Playwright über PuppenspielerSharp-?

Don-'-t bekommen Sie mich falsch PuppeteerSharp ist exzellent, wenn Sie nur Chrome benötigen, aber hier, wenn Playwright Sinn macht.

Das Problem des Multi---Browsers

Ihre Benutzer verwenden alle Chrome, die Sie benutzen

  • Chrom-/.Edge,: , , ~65% , von den Benutzern ,( ,Chromium , - , auf der Grundlage von ) ,
  • Safari.: . . ~20% . der Benutzer . ( . vor allem mobile
  • Firefox.: . . ~5% . der Benutzer .( , aber oft andere Rendering .

Ein Fehler, der nur in Safari angezeigt wird, kann Sie verlieren 20%. von Ihren potentiellen Benutzern Mit dem Playwright können Sie alle drei mit derselben API testen.

Bessere Entwicklererfahrung

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

Die wichtigsten Vorteile

  1. Unterstützung für Multi--.browser Testen Sie Chrome , , , Firefox , , , Safari mit identischem Code
  2. Bessere Auto-Wartung Verlässlicher aus dem Kasten weniger schuppige Tests.
  3. Betrachter verfolgen Bezeichnen Sie vollständige Spuren von Testläufen zum Debuggen
  4. Codegen Erzeugen Sie Testcode durch Interaktion mit Ihrer Website
  5. Netzabhörung Stärker als PuppenspielerSharp
  6. Moderne Web-Funktionen Bessere Unterstützung für Schatten-DOM, ,, iframes, usw..

Einrichten des Playwrights für .NET

Installieren Microsoft®.-Playwright NuGet-Paket„:"

dotnet add package Microsoft.Playwright
dotnet add package Microsoft.Playwright.NUnit  # Or use xUnit

Installieren Sie dann die Browser dieses Downloads Chromium, FirefoxL 347 vom 20.12.2013, S. 671. WebKit):

pwsh bin/Debug/net9.0/playwright.ps1 install

Oder unter Linux.

playwright install

Anmerkung: Die erste Browser-Installation Downloads rund um 400.MB... Nachfolgende Updates sind viel kleiner.

Hier sind meine Test-Projektkonfigurationen.

<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'-M mit xEinheitAber Playwright funktioniert genauso gut mit NUnit oder MSTestTatsächlich hat der Playwright einen engagierten NUnit-Integration mit zusätzlichen Helfern

Erstellen einer Basistestklasse

Ähnlich wie PuppeteerSharp,, jedoch mit Multi--Browser Unterstützung

Die Klassenstruktur

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
}

Warum Browser-Kontexten?

Beachten wir, dass wir eine Context vor der Erstellung einer PageDies ist ein Playwright-Konzept, das PuppeteerSharp nicht hat.

  • Kontext Getrennte Browser-Session - Inkognito-Modus - )
  • Seite Eine Registerkarte in diesem Kontext

Kontexte lassen dich

  • Testen Sie mit verschiedenen Benutzersitzungen gleichzeitig
  • Setzen Sie verschiedene Cookies, die für den Kontext verwendet werden
  • Isolationstests komplett -(-even innerhalb der gleichen Browser-Instanz - )-

Hilfsmethoden

// 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();
}

Ortungsgeräte für die geheime Waffe des Spielers

Beachten Sie die Page.Locator() calls-?-Dies unterscheidet sich von PuppeteerSharp-' QuerySelectorOrtungen sind ,:

  • Faul Sie fragen das DOM nicht ab, bis du sie benutzt.
  • Wiederholung Sie warten automatisch und wiederholen Operationen
  • Streng Sie fälschen, wenn mehrere Elemente zusammenpassen.
// PuppeteerSharp way (manual waiting)
await Page.WaitForSelectorAsync("#button");
await Page.ClickAsync("#button");

// Playwright way (auto-waiting)
await Page.Locator("#button").ClickAsync(); // Waits automatically!

Prüfung des Multi---Browsers

Hier, wo Playwright glänzt, können Sie den gleichen Test über alle Browser durchführen.

Verwendung von xUnit-Theorie für Multi-Tests

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}");
    }
}

Browser-Spezifische Testklassen

Oder separate Testklassen für jeden Browser erstellen

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);
    }
}

Schreiben von Tests Die Playwright-Weg

Betrachten wir reale Beispiele, die Playwright-Vorteile zeigen

Testen von HTMX mit Auto.--Warten

[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);
}

Prüfung der Wechselwirkungen von Alpine-.

Handgriffe für Playwright Alpen-.-js Reaktivität und Animationen nahtlos

[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);
}

Prüfformulare mit Validierung

[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();
}

Erweiterte Playwright-Funktionen

Netzwerk-Abfangleistung ---- Leistungsstärker als PuppenspielerSharp

[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);
}

Trace Viewer - Fehler beim Debug fehlgeschlagene Tests

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();
}

Dann sehen Sie die Spur.

playwright show-trace traces/TestName.zip

Dies öffnet eine Benutzeroberfläche, die zeigt:

  • Jede Aktion, die Ihr Test durchgeführt hat
  • Screenshots bei jedem Schritt
  • Netzanfragen
  • Konsolenprotokolle
  • DOM Snapshots

Es ist absolut brillant für Debugging.

Screenshots zum Scheitern

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();
}

Mobile Emulation

[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();
}

Playwright kommt mit Gerätedeskriptoren für„:"

  • iPhone , 13, , , 13, Pro , ,, MSSK3, 11,, SE
  • iPad , , , iPad Pro
  • Samsung Galaxy-Pixel
  • Und viele mehr

Prüfung des Dunklen Modus

[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 vs PuppenspielerSharp Seite für Seite

Hier ist der gleiche Test in beiden Bibliotheken, um die Unterschiede zu zeigen.

PuppenspielerSharp Version

[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);
}

Wiedergaberecht-Version

[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=.*"));
}

Hinweis:

  • Kein Handbuch WaitForSelectorAsync erforderlich
  • Nein Task.Delay erforderlich
  • Saubere Behauptungen mit Expect
  • Mehr lesbar mit has-text Wählgerät

PDF-Erstellung mit Playwright

Wie PuppeteerSharp-,-Playwright PDFs generieren kann, ist die API fast identisch.

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"
        }
    });
}

Das gleiche PDF gotchas aus dem PuppenspielerSharp Artikel Auch hier anwenden

Wann man Playwright wählt

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

Wählen Sie Playwright, wenn -:

  • Sie benötigen Multi---Browser-Test
  • Sie wollen bessere Debugging-Tools .( .Trace Viewer .
  • Sie testen komplexe moderne Web-Apps
  • Sie brauchen mobile Emulation
  • Sie wollen zuverlässigere Tests aus dem Kasten heraus

Wählen Sie PuppeteerSharp, wenn

  • Chrome-/.Edge ist nur in Ordnung
  • Sie wollen einfachere Einrichtung
  • Sie sind bereits vertraut mit Puppeteer
  • Sie brauchen etwas weniger Ressourcenaufwand

Laufen in CI-/-CD

Beispiel für GitHub-Aktionen

Playwright arbeitet nahtlos in CI-/-CD-Pipelines, hier ist ein komplettes GitHub-Maßnahmen Arbeitsablauf

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

Docker-Unterstützung

Durchführung von Playwright Tests in Docker erfordert die Installation von Systemabhängigkeiten

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"]

Leistungsvergleich

Von meinem Testen auf diesem Blog

Beschreibung des Merkmals
Chrom-Prüfgeschwindigkeit -~2.5-M -MNSK3-MSSK4-MUSK2
Multi---Browser Weitere Informationen: http:europa.eu.int/en/comm/index_de.htm
Auto---Wartesicherheit Sehr gut, sehr gut
Speicher pro Browser -MB- -Mb-~180-MB
Komplexität einrichten Einfaches, mittelschweres und leichtes
Debugging-Werkzeuge Chrome-DevTools - Spurenbetrachter
Mobile Emulation Grundlegende Informationen Ausgezeichnet

Playwright ist etwas langsamer und verwendet ein wenig mehr Speicher, aber die Zuverlässigkeit und Debugging Vorteile in der Regel überwiegen diese

Häufig Gotchas

Strikter Modus

Playwright-Lokatoren von ' sind standardmäßig streng

// 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();

Installation des Browsers

Nicht vergessen zu laufen playwright install nach dem Hinzufügen des Pakets... die Browser sind... die im NuGet Paket enthalten sind...

Kontext vs Seite

Kontexte isolieren Tests speichern

// 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

Unterschiede des Webkits

Safari (-WebKit)- kann anders verhalten

// 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();

Schlußfolgerung

Playwright stellt die Entwicklung der Browser-Automatisierung für Entwickler von .-NET dar, wenn Sie multi--browser Unterstützung benötigen.,e es ist die klare Wahl... PuppeteerSharpDie Vorteile sind jedoch erheblich.

  • Test über alle gängigen Browser mit identischem Code
  • Bessere Zuverlässigkeit durch automatisches Warten
  • Ausgezeichnetes Debugging mit Trace Viewer
  • Mobile und Tablet-Emulation aus dem Kasten
  • Belastbarer gegenüber zeitlichen Problemen

Meine Empfehlung

  • Neue Projekte, die Cross-Tests benötigenBeginnen Sie mit dem Playwright
  • Nur Projekte von Chrome--PuppeteerSharp ist einfacher
  • Bestehende PuppenspielerSharp-ProjekteMigrieren nur, wenn Sie Multi--browser benötigen
  • Komplexe moderne Web-AppsPlaywright-Tooling ist das Overhead wert

Der Trace-Betrachter allein hat mir Stunden des Debuggens gespart. ,. , in der Lage zu sein, einen fehlgeschlagenen Test erneut abzuspielen und genau zu sehen, was der Browser bei jedem Schritt sah ist unschätzbar

Give Playwright a go on your next project Mehr erfahren Sie werden angenehm überrascht sein, wie viel einfacher es macht Cross--Browser-Testing

Literaturhinweise

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.