Multi-Browser EM SK1E Essai avec Playwright pour .NET (Français (French))

Multi-Browser EM SK1E Essai avec Playwright pour .NET

Thursday, 27 November 2025

//

20 minute read

Playwright est la solution officielle de Microsoft' pour l’automatisation des navigateurs modernes , offrant un support multilatéral-browser MSC3ChromeM SK4Firefox+,Safari*) avec une seule APIMST7 construite MST8dans le domaine du débugging des traces+MST9 et de l’émulation mobile+Mst10 Ce guide couvre tout, depuis l’installation jusqu’aux modèles d’essais avancésMst11 en comparant avec PuppeteerSharp afin de vous aider à choisir l’outil appropriéM st12 Si vous n’avez qu’à utiliser Chrome et que vous voulez une configuration plus simpleM ST13 envisagez Écarpe de marionnettes au lieu - mais si vous avez besoin d’une analyse transversale exhaustive-essais de navigateurM SK2 Le playwright est la voie à suivre

Introduction

Si vous' avez lu mon article sur Écarpe de marionnettes, vous ' sachez que je'je suis un fan de l’E moderneM SK3outils d’essai qui ne permettent pas à 't de vous détruire les cheveuxMSC5 PuppeteerSharp est brillant pour ChromeMST6seulement à l’essaisMSP7 mais qu’il en va si vous avez besoin de tester sur plusieurs navigateursMSS8 QueMSSS9est là où Playwright arrive en.

Playwright est Microsoft' la réponse au problème de l’automatisation du navigateur , et ils' ont appris à partir de tout ce qui a été précédemment à lâ€TMégard de . Câ€TMest ce que fait ' comme PuppeteerSharpM SK5 son frère plus ambitieux |- c'est tout ce que le puppeteer fait | , mais il ajoute des supports Firefox et Safari |

Dans cet article, je vous montrerai comment utiliser Playwright pour .NET afin de tester à travers ChromeM SK3 Firefox, et SafariMSC5 avec des exemples de code réels et des modèles pratiques que vous pouvez utiliser aujourd’hui .

Pourquoi Playwright sur PuppeteerSharp?

Ne me trompez pas - Écarpe de marionnettes est excellent si vous n’avez besoin que de Chrome. Mais ici's quand Playwright a sensM SK2

Le problème du navigateur multi-

Vos utilisateurs n’utilisent pas tous le Chrome' Tous les utilisateurs utilisent

  • Chrome/Edge: ~65% d'utilisateurs
  • Safari: ~20% d’utilisateurs
  • Firefox: ~5% d'utilisateurs ( mais souvent une traduction différente)

Un bug qui n'apparaît que dans Safari peut vous perdre 20% de vos utilisateurs potentiels. Playwright vous permet d'essayer les trois avec la même API

Améliorer l'expérience des développeurs

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

Avantages clés

  1. Multi-Support du navigateur - Test ChromeM SK1 Firefox, Safari avec code identique
  2. Better auto-attendre - Plus fiable à l’extérieur de la boîte
  3. Vérificateur des traces - Enregistrez les traces complètes des essais de débugging
  4. Codegéne - Gérer le code d'essai en interagissant avec votre site
  5. Interception du réseau - Plus puissant que PuppeteerSharp
  6. Caractéristiques modernes du Web - Amélioration de l'appui pour le DOM-shadow, iframesMSC2 etcM SK3

Établissement d'un dramaturge pour .NET

Installer le Microsoft.Playwright NuGet package:

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

Ensuite installez les navigateurs (cette téléchargement Chromium, Firefox, et WebKit):

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

Ou sur Linux/Mac:

playwright install

Note: Le premier navigateur installe les téléchargements autour de 400MBM SK1 Les mises à jour subséquentes sont beaucoup plus petites.

Voici la configuration de mon projet d’essai'

<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 utilisant xUnit, mais Playwright fonctionne aussi bien avec NUnit ou Essai MS. En fait, Le dramaturge a un Intégration NUnit avec des aides supplémentaires.

Création d'une classe d'essai de base

Il s'agit d'une approche similaire à Écarpe de marionnettes, mais avec plusieurs supports de navigateurM SK1browser:

Structure de la classe

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
}

Pourquoi les contextes du navigateur?

Veuillez noter que nous avons créé Context avant la création d'un Page. C’est un concept de dramaturge que PuppeteerSharp ne possède pas.

  • Contexte = Séance de navigateur isolé (comme mode incognito)
  • Pages = Un onglet dans ce contexte

Les contextes vous permettent:

  • Tester avec différentes sessions d'utilisateur simultanément
  • Établir des cookies différents, permissions, ou locale par contexte
  • Isoler complètement les essais (même dans la même instance de navigateur)

Méthodes d'aide

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

Locateurs - Playwright 's Secret Weapon

Veuillez noter que Page.Locator() appels? Ceci diffère de PuppeteerSharp's QuerySelector. Les locateurs sontM SK1

  • Lazy - Ils ne demandent pas de DOM jusqu’à ce que vous les utilisez
  • En essayant de nouveau - Ils attendent et essaient automatiquement les opérations
  • Strict - Il y a des erreurs si plusieurs éléments correspondent à ceux-ci.
// PuppeteerSharp way (manual waiting)
await Page.WaitForSelectorAsync("#button");
await Page.ClickAsync("#button");

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

Multi-Examen du navigateur

Vous pouvez faire le même test sur tous les navigateurs.

Utilisation de la théorie xUnit pour les tests multi-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}");
    }
}

Browser-Classes d'essai spécifiques

Ou créer des classes d’essai distinctes pour chaque navigateur:

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

Tests de rédaction - La façon d’écrire

Let's voir des exemples réels montrant les avantages de Playwright's

Tester HTMX avec Auto-Waiting

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

Évaluation des interactions Alpine.js

Méthodes de manipulation Alpine.js réactivité et animations sans fil:

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

Évaluation des formulaires avec validation

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

Caractéristiques avancées du dramaturge

Interception du réseau - Plus puissant que PuppeteerSharp

[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 - Des essais de débugging ont échoué

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

Ensuite, voir la trace:

playwright show-trace traces/TestName.zip

Ceci ouvre une interface montrant:

  • Toutes les mesures que votre test a prises
  • Screenshots à chaque étape
  • Demandes de réseau
  • Logs de la console
  • DOM snapshots

Il'est absolument brillant pour le débugging.

Screenshots sur le défaut

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

Emulation mobile

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

L'auteur joue avec descripteurs d'appareils pour:

  • iPhone 13,
  • iPad, iPad Pro
  • Samsung Galaxy, Pixel
  • Et beaucoup plus

Évaluation du mode foncé

[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 PuppeteerSharp - Side by Side

Voici' est le même test dans les deux bibliothèques pour montrer les différencesM SK1

Version PuppeteerSharp

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

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

Avis:

  • Aucun manuel WaitForSelectorAsync d'un État membre
  • Non Task.Delay d'un État membre
  • Assertions plus propres avec Expect
  • Plus lisible avec has-text Détermination de l'échantillon

Production de PDF avec Playwright

Comme PuppeteerSharp, Le Playwright peut générer des PDFs . L’API est presque identique:

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

Le même PDF de l'article PuppeteerSharp Appliquez ici aussi.

Quand choisir le dramaturge

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

Choisissez Playwright lorsque:

  • Vous avez besoin de tests multi-browser
  • Vous voulez des outils de débugging meilleurs (trace viewer)
  • Vous testez des applications Web modernes complexes
  • Vous avez besoin d'une emulation mobile
  • Vous voulez des tests plus fiables hors de la boîte

Choisissez PuppeteerSharp lorsque:

  • Chrome/L'Edge seulement est bon
  • Vous voulez une configuration plus simple
  • Vous' êtes déjà familiers avec Puppeteer
  • Vous avez besoin de moins de ressources supplémentaires

Fonctionnement dans CI/CD

Exemple d'actions de GitHub

Le playwright travaille sans fil dans les pipelines de CI/CDM SK1C'est ici Actions de GitHub flux de travail:

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

Soutien du docker

Exécuter les tests de Playwright dans Docker Il faut installer des dépendances de système:

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

Comparaison du rendement

De mes essais sur ce blog:

Caractéristique PuppeteerSharp MSC2 Playwright MSC3
La vitesse d'essai Chrome ~2.5s
Multi-browser ❌ ≥ ≤✓ (adds ~1s par navigateur
Auto-attendant la fiabilité Bonne (manuelle) Excellente MSC4automatiqueMSc5
Mémoire par navigateur ~150MB
Complexité d'installation simples modérées
Outils de débugging Chrome DevTools Trace Viewer
Émulation mobile Basic Excellent

Playwright est légèrement plus lente et utilise un peu plus de mémoire, mais les avantages liés à la fiabilité et au débugging sont habituellement supérieurs à ceux qui y sont associés.

Gotchas communs

1. Mode strict

Playwright'les locateurs sont stricts par défaut:

// 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. Installation du navigateur

Don' ne pas oublier de courir playwright install après l’ajout du paquet. Les navigateurs ne sont pas inclus dans le paquet NuGet'

3. Contexte vs Page

Rappeler les contextes isolant les tests:

// 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. Différences de la trousse Web

Safari (WebKit) peut se comporter différemment

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

Conclusion

Playwright représente l’évolution de l’automatisation des navigateurs pour les développeurs .NETM SK1 Si vous avez besoin d’un support multilatéral -browser Écarpe de marionnettes, mais les avantages sont substantielsM SK1

  • Tester tous les principaux navigateurs avec un code identique
  • Une plus grande fiabilité grâce à l'attente automatique
  • Excellente débugging avec trace viewer
  • Emulation mobile et tablette hors de la boîte
  • Une plus grande résilience aux problèmes liés au calendrier

Ma recommandation:

  • Nouveaux projets nécessitant des tests croisés de navigateur: Démarrer par Playwright
  • Chrome-projets seulement: PuppeteerSharp est plus simple
  • Projets existants de PuppeteerSharp: Migrant seulement si vous avez besoin d’un navigateur multi-
  • Applications Web modernes complexes: PlaywrightThe tooling of ' is worth the overhead

Le seul lecteur de trace m’a sauvé des heures de débugging. Être en mesure de répéter un essai manquant et de voir exactement ce que le navigateur a vu à chaque étape est inestimable.

Donnez à Playwright un coup d’oeil sur votre prochain projet - vousM SK1 vous serez agréablement surpris de la facilité qu’il permet de faire le croisement

Autres lectures

Finding related posts...
logo

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