Skip to content

Infra: Migrate test framework from xUnit to NUnit (modeled after puppeteer-sharp) #71

@kblok

Description

@kblok

Description

Migrate PlaywrightSharp.Tests from xUnit to NUnit 4.x, following the same patterns used in puppeteer-sharp. This is a large structural change that touches every test file.

What to Implement

1. Update package references

In PlaywrightSharp.Tests.csproj:

  • Remove: xunit, xunit.runner.visualstudio, MicrosoftExtensions.Logging.Xunit
  • Add: NUnit (4.1.0+), NUnit3TestAdapter (4.6.0+), NUnit.Analyzers (4.3.0+)
  • Keep: Microsoft.NET.Test.Sdk

2. Create PlaywrightSharp.Nunit project

New project mirroring puppeteer-sharp's PuppeteerSharp.Nunit:

  • PlaywrightTestAttribute — custom test attribute that:
    • Maps upstream test file + test name for traceability
    • Reads PRODUCT env var (CHROMIUM/FIREFOX/WEBKIT)
    • Reads HEADLESS env var
    • Loads test expectations from embedded JSON files for platform-specific skip/fail management
  • Test expectation JSON files (TestExpectations.upstream.json, TestExpectations.local.json)

3. Rewrite test base classes

Replace xUnit IAsyncLifetime + ICollectionFixture pattern with NUnit equivalents:

xUnit NUnit
IAsyncLifetime.InitializeAsync [SetUp] async Task
IAsyncLifetime.DisposeAsync [TearDown] async Task
ICollectionFixture<T> [SetUpFixture] with [OneTimeSetUp]/[OneTimeTearDown]
[Collection("name")] Namespace-based fixture scoping
[Fact] / [Retry] [Test] / [PlaywrightTest]
[Trait("Category", "chromium")] [Category("chromium")]
ITestOutputHelper TestContext.Out

New base class hierarchy:

  • PlaywrightSharpBaseTest[SetUp]: workspace, server reset. Provides Server, HttpsServer
  • PlaywrightSharpBrowserBaseTest[SetUp]: launch browser. [TearDown]: dispose. Provides Browser
  • PlaywrightSharpBrowserContextBaseTest[SetUp]: new context. Provides Context
  • PlaywrightSharpPageBaseTest[SetUp]: new page. [TearDown]: close page. Provides Page

4. Create SetUpFixture for global setup

Replace PlaywrightSharpLoader and collection fixtures:

[SetUpFixture]
public class TestServerSetup
{
    [OneTimeSetUp]
    public async Task GlobalSetup()
    {
        // Download browsers
        // Start HTTP/HTTPS servers
    }

    [OneTimeTearDown]
    public async Task GlobalTeardown()
    {
        // Stop servers
    }
}

5. Migrate all test files (~176 files)

For each test file:

  • Remove [Collection(...)] attribute
  • Replace [Trait("Category", "chromium")][Category("chromium")]
  • Replace [Retry] / [Fact][Test, PlaywrightTest("file.spec", "test name")]
  • Replace constructor (ITestOutputHelper output) : base(output) → parameterless constructor
  • Replace Assert.Equal(a, b)Assert.That(b, Is.EqualTo(a))
  • Replace Assert.True(x)Assert.That(x, Is.True)
  • Replace Assert.Contains(x, col)Assert.That(col, Does.Contain(x))
  • Replace Assert.Throws<T>Assert.ThrowsAsync<T> or Assert.That(() => ..., Throws.TypeOf<T>())

6. Remove custom xUnit framework

Delete:

  • PlaywrightXunitTestFramework.cs
  • PlaywrightXunitTestFrameworkExecutor.cs
  • PlaywrightXunitTestAssemblyRunner.cs
  • RetryAttribute.cs, RetryFactDiscoverer.cs, RetryTestCase.cs
  • DelayedMessageBus.cs
  • Collection definition files

7. Configure test parallelization

  • NUnit default is parallel; add [assembly: NonParallelizable] or configure in .runsettings to match current sequential execution behavior
  • Consider [Parallelizable(ParallelScope.None)] on base class

Upstream References

  • puppeteer-sharp lib/PuppeteerSharp.Tests/ (NUnit test patterns)
  • puppeteer-sharp lib/PuppeteerSharp.Nunit/ (custom attribute infrastructure)
  • puppeteer-sharp lib/PuppeteerSharp.Tests/PuppeteerBaseTest.cs (base class hierarchy)
  • puppeteer-sharp lib/PuppeteerSharp.Tests/TestServerSetup.cs (global setup fixture)

Files to Create

  • src/PlaywrightSharp.Nunit/PlaywrightSharp.Nunit.csproj (new project)
  • src/PlaywrightSharp.Nunit/PlaywrightTestAttribute.cs
  • src/PlaywrightSharp.Nunit/TestExpectations/ (expectation JSONs)

Files to Modify

  • src/PlaywrightSharp.Tests/PlaywrightSharp.Tests.csproj
  • src/PlaywrightSharp.Tests/BaseTests/PlaywrightSharpBaseTest.cs
  • src/PlaywrightSharp.Tests/BaseTests/PlaywrightSharpBrowserBaseTest.cs
  • src/PlaywrightSharp.Tests/BaseTests/PlaywrightSharpPageBaseTest.cs
  • All ~176 test files in src/PlaywrightSharp.Tests/
  • src/PlaywrightSharp.sln (add new project)

Files to Delete

  • Custom xUnit framework files (RetryAttribute, DelayedMessageBus, etc.)
  • Collection definition files

Verification

dotnet build ./src/PlaywrightSharp.sln
dotnet test ./src/PlaywrightSharp.Tests/PlaywrightSharp.Tests.csproj --filter Category=chromium -c Debug -f net10.0
PRODUCT=FIREFOX dotnet test ./src/PlaywrightSharp.Tests/PlaywrightSharp.Tests.csproj --filter Category=firefox -c Debug -f net10.0

Dependencies

Depends on: #1

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature-parityIssues tracking feature parity with upstream Playwrightphase-1Phase 1: Infrastructure

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions