docs(dotnet): recommend MSTest over NUnit (#31245)

This commit is contained in:
Max Schmitt 2024-06-11 15:06:03 +02:00 committed by GitHub
parent 664c4dd442
commit f95b4e0ac8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 318 additions and 319 deletions

View File

@ -16,7 +16,7 @@ A few examples where it may come in handy:
All of that could be achieved via [APIRequestContext] methods.
The following examples rely on the [`Microsoft.Playwright.NUnit`](./test-runners.md) package which creates a Playwright and Page instance for each test.
The following examples rely on the [`Microsoft.Playwright.MSTest`](./test-runners.md) package which creates a Playwright and Page instance for each test.
<!-- TOC -->
@ -34,22 +34,19 @@ The following example demonstrates how to use Playwright to test issues creation
GitHub API requires authorization, so we'll configure the token once for all tests. While at it, we'll also set the `baseURL` to simplify the tests.
```csharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Playwright.NUnit;
using Microsoft.Playwright;
using NUnit.Framework;
using Microsoft.Playwright.MSTest;
namespace PlaywrightTests;
[TestClass]
public class TestGitHubAPI : PlaywrightTest
{
static string API_TOKEN = Environment.GetEnvironmentVariable("GITHUB_API_TOKEN");
static string? API_TOKEN = Environment.GetEnvironmentVariable("GITHUB_API_TOKEN");
private IAPIRequestContext Request = null;
private IAPIRequestContext Request = null!;
[SetUp]
[TestInitialize]
public async Task SetUpAPITesting()
{
await CreateAPIRequestContext();
@ -71,7 +68,7 @@ public class TestGitHubAPI : PlaywrightTest
});
}
[TearDown]
[TestCleanup]
public async Task TearDownAPITesting()
{
await Request.DisposeAsync();
@ -83,36 +80,34 @@ public class TestGitHubAPI : PlaywrightTest
Now that we initialized request object we can add a few tests that will create new issues in the repository.
```csharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Text.Json;
using Microsoft.Playwright.NUnit;
using Microsoft.Playwright;
using NUnit.Framework;
using Microsoft.Playwright.MSTest;
namespace PlaywrightTests;
[TestFixture]
[TestClass]
public class TestGitHubAPI : PlaywrightTest
{
static string REPO = "test-repo-2";
static string REPO = "test";
static string USER = Environment.GetEnvironmentVariable("GITHUB_USER");
static string API_TOKEN = Environment.GetEnvironmentVariable("GITHUB_API_TOKEN");
static string? API_TOKEN = Environment.GetEnvironmentVariable("GITHUB_API_TOKEN");
private IAPIRequestContext Request = null;
private IAPIRequestContext Request = null!;
[Test]
[TestMethod]
public async Task ShouldCreateBugReport()
{
var data = new Dictionary<string, string>();
data.Add("title", "[Bug] report 1");
data.Add("body", "Bug description");
var data = new Dictionary<string, string>
{
{ "title", "[Bug] report 1" },
{ "body", "Bug description" }
};
var newIssue = await Request.PostAsync("/repos/" + USER + "/" + REPO + "/issues", new() { DataObject = data });
Assert.True(newIssue.Ok);
await Expect(newIssue).ToBeOKAsync();
var issues = await Request.GetAsync("/repos/" + USER + "/" + REPO + "/issues");
Assert.True(issues.Ok);
await Expect(newIssue).ToBeOKAsync();
var issuesJsonResponse = await issues.JsonAsync();
JsonElement? issue = null;
foreach (JsonElement issueObj in issuesJsonResponse?.EnumerateArray())
@ -125,23 +120,24 @@ public class TestGitHubAPI : PlaywrightTest
}
}
}
Assert.NotNull(issue);
Assert.IsNotNull(issue);
Assert.AreEqual("Bug description", issue?.GetProperty("body").GetString());
}
[Test]
[TestMethod]
public async Task ShouldCreateFeatureRequests()
{
var data = new Dictionary<string, string>();
data.Add("title", "[Feature] request 1");
data.Add("body", "Feature description");
var data = new Dictionary<string, string>
{
{ "title", "[Feature] request 1" },
{ "body", "Feature description" }
};
var newIssue = await Request.PostAsync("/repos/" + USER + "/" + REPO + "/issues", new() { DataObject = data });
Assert.True(newIssue.Ok);
await Expect(newIssue).ToBeOKAsync();
var issues = await Request.GetAsync("/repos/" + USER + "/" + REPO + "/issues");
Assert.True(issues.Ok);
await Expect(newIssue).ToBeOKAsync();
var issuesJsonResponse = await issues.JsonAsync();
var issuesJson = (await issues.JsonAsync())?.EnumerateArray();
JsonElement? issue = null;
foreach (JsonElement issueObj in issuesJsonResponse?.EnumerateArray())
@ -154,7 +150,7 @@ public class TestGitHubAPI : PlaywrightTest
}
}
}
Assert.NotNull(issue);
Assert.IsNotNull(issue);
Assert.AreEqual("Feature description", issue?.GetProperty("body").GetString());
}
@ -167,41 +163,47 @@ public class TestGitHubAPI : PlaywrightTest
These tests assume that repository exists. You probably want to create a new one before running tests and delete it afterwards. Use `[SetUp]` and `[TearDown]` hooks for that.
```csharp
using System.Text.Json;
using Microsoft.Playwright;
using Microsoft.Playwright.MSTest;
namespace PlaywrightTests;
[TestClass]
public class TestGitHubAPI : PlaywrightTest
{
// ...
// ...
[TestInitialize]
public async Task SetUpAPITesting()
{
await CreateAPIRequestContext();
await CreateTestRepository();
}
[SetUp]
public async Task SetUpAPITesting()
{
await CreateAPIRequestContext();
await CreateTestRepository();
}
private async Task CreateTestRepository()
{
var resp = await Request.PostAsync("/user/repos", new()
{
DataObject = new Dictionary<string, string>()
{
["name"] = REPO,
},
});
await Expect(resp).ToBeOKAsync();
}
private async Task CreateTestRepository()
{
var resp = await Request.PostAsync("/user/repos", new()
{
DataObject = new Dictionary<string, string>()
{
["name"] = REPO,
},
});
Assert.True(resp.Ok);
}
[TestCleanup]
public async Task TearDownAPITesting()
{
await DeleteTestRepository();
await Request.DisposeAsync();
}
[TearDown]
public async Task TearDownAPITesting()
{
await DeleteTestRepository();
await Request.DisposeAsync();
}
private async Task DeleteTestRepository()
{
var resp = await Request.DeleteAsync("/repos/" + USER + "/" + REPO);
Assert.True(resp.Ok);
}
private async Task DeleteTestRepository()
{
var resp = await Request.DeleteAsync("/repos/" + USER + "/" + REPO);
await Expect(resp).ToBeOKAsync();
}
}
```
@ -210,36 +212,34 @@ public class TestGitHubAPI : PlaywrightTest
Here is the complete example of an API test:
```csharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Text.Json;
using Microsoft.Playwright.NUnit;
using Microsoft.Playwright;
using NUnit.Framework;
using Microsoft.Playwright.MSTest;
namespace PlaywrightTests;
[TestFixture]
[TestClass]
public class TestGitHubAPI : PlaywrightTest
{
static string REPO = "test-repo-2";
static string USER = Environment.GetEnvironmentVariable("GITHUB_USER");
static string API_TOKEN = Environment.GetEnvironmentVariable("GITHUB_API_TOKEN");
static string? API_TOKEN = Environment.GetEnvironmentVariable("GITHUB_API_TOKEN");
private IAPIRequestContext Request = null;
private IAPIRequestContext Request = null!;
[Test]
[TestMethod]
public async Task ShouldCreateBugReport()
{
var data = new Dictionary<string, string>();
data.Add("title", "[Bug] report 1");
data.Add("body", "Bug description");
var data = new Dictionary<string, string>
{
{ "title", "[Bug] report 1" },
{ "body", "Bug description" }
};
var newIssue = await Request.PostAsync("/repos/" + USER + "/" + REPO + "/issues", new() { DataObject = data });
Assert.True(newIssue.Ok);
await Expect(newIssue).ToBeOKAsync();
var issues = await Request.GetAsync("/repos/" + USER + "/" + REPO + "/issues");
Assert.True(issues.Ok);
await Expect(newIssue).ToBeOKAsync();
var issuesJsonResponse = await issues.JsonAsync();
JsonElement? issue = null;
foreach (JsonElement issueObj in issuesJsonResponse?.EnumerateArray())
@ -252,23 +252,24 @@ public class TestGitHubAPI : PlaywrightTest
}
}
}
Assert.NotNull(issue);
Assert.IsNotNull(issue);
Assert.AreEqual("Bug description", issue?.GetProperty("body").GetString());
}
[Test]
[TestMethod]
public async Task ShouldCreateFeatureRequests()
{
var data = new Dictionary<string, string>();
data.Add("title", "[Feature] request 1");
data.Add("body", "Feature description");
var data = new Dictionary<string, string>
{
{ "title", "[Feature] request 1" },
{ "body", "Feature description" }
};
var newIssue = await Request.PostAsync("/repos/" + USER + "/" + REPO + "/issues", new() { DataObject = data });
Assert.True(newIssue.Ok);
await Expect(newIssue).ToBeOKAsync();
var issues = await Request.GetAsync("/repos/" + USER + "/" + REPO + "/issues");
Assert.True(issues.Ok);
await Expect(newIssue).ToBeOKAsync();
var issuesJsonResponse = await issues.JsonAsync();
var issuesJson = (await issues.JsonAsync())?.EnumerateArray();
JsonElement? issue = null;
foreach (JsonElement issueObj in issuesJsonResponse?.EnumerateArray())
@ -281,11 +282,11 @@ public class TestGitHubAPI : PlaywrightTest
}
}
}
Assert.NotNull(issue);
Assert.IsNotNull(issue);
Assert.AreEqual("Feature description", issue?.GetProperty("body").GetString());
}
[SetUp]
[TestInitialize]
public async Task SetUpAPITesting()
{
await CreateAPIRequestContext();
@ -294,14 +295,16 @@ public class TestGitHubAPI : PlaywrightTest
private async Task CreateAPIRequestContext()
{
var headers = new Dictionary<string, string>();
// We set this header per GitHub guidelines.
headers.Add("Accept", "application/vnd.github.v3+json");
// Add authorization token to all requests.
// Assuming personal access token available in the environment.
headers.Add("Authorization", "token " + API_TOKEN);
var headers = new Dictionary<string, string>
{
// We set this header per GitHub guidelines.
{ "Accept", "application/vnd.github.v3+json" },
// Add authorization token to all requests.
// Assuming personal access token available in the environment.
{ "Authorization", "token " + API_TOKEN }
};
Request = await this.Playwright.APIRequest.NewContextAsync(new()
Request = await Playwright.APIRequest.NewContextAsync(new()
{
// All requests we send go to this API endpoint.
BaseURL = "https://api.github.com",
@ -318,10 +321,10 @@ public class TestGitHubAPI : PlaywrightTest
["name"] = REPO,
},
});
Assert.True(resp.Ok);
await Expect(resp).ToBeOKAsync();
}
[TearDown]
[TestCleanup]
public async Task TearDownAPITesting()
{
await DeleteTestRepository();
@ -331,7 +334,7 @@ public class TestGitHubAPI : PlaywrightTest
private async Task DeleteTestRepository()
{
var resp = await Request.DeleteAsync("/repos/" + USER + "/" + REPO);
Assert.True(resp.Ok);
await Expect(resp).ToBeOKAsync();
}
}
```
@ -344,21 +347,23 @@ project to check that it appears at the top of the list. The check is performed
```csharp
class TestGitHubAPI : PageTest
{
[Test]
public async Task LastCreatedIssueShouldBeFirstInTheList()
{
var data = new Dictionary<string, string>();
data.Add("title", "[Feature] request 1");
data.Add("body", "Feature description");
var newIssue = await Request.PostAsync("/repos/" + USER + "/" + REPO + "/issues", new() { DataObject = data });
Assert.True(newIssue.Ok);
[TestMethod]
public async Task LastCreatedIssueShouldBeFirstInTheList()
{
var data = new Dictionary<string, string>
{
{ "title", "[Feature] request 1" },
{ "body", "Feature description" }
};
var newIssue = await Request.PostAsync("/repos/" + USER + "/" + REPO + "/issues", new() { DataObject = data });
await Expect(newIssue).ToBeOKAsync();
// When inheriting from 'PlaywrightTest' it only gives you a Playwright instance. To get a Page instance, either start
// a browser, context, and page manually or inherit from 'PageTest' which will launch it for you.
await Page.GotoAsync("https://github.com/" + USER + "/" + REPO + "/issues");
var firstIssue = Page.Locator("a[data-hovercard-type='issue']").First;
await Expect(firstIssue).ToHaveTextAsync("[Feature] request 1");
}
// When inheriting from 'PlaywrightTest' it only gives you a Playwright instance. To get a Page instance, either start
// a browser, context, and page manually or inherit from 'PageTest' which will launch it for you.
await Page.GotoAsync("https://github.com/" + USER + "/" + REPO + "/issues");
var firstIssue = Page.Locator("a[data-hovercard-type='issue']").First;
await Expect(firstIssue).ToHaveTextAsync("[Feature] request 1");
}
}
```
@ -368,22 +373,23 @@ The following test creates a new issue via user interface in the browser and the
it was created:
```csharp
// Make sure to extend from PageTest if you want to use the Page class.
class GitHubTests : PageTest
{
[Test]
public async Task LastCreatedIssueShouldBeOnTheServer()
{
await Page.GotoAsync("https://github.com/" + USER + "/" + REPO + "/issues");
await Page.Locator("text=New Issue").ClickAsync();
await Page.Locator("[aria-label='Title']").FillAsync("Bug report 1");
await Page.Locator("[aria-label='Comment body']").FillAsync("Bug description");
await Page.Locator("text=Submit new issue").ClickAsync();
String issueId = Page.Url.Substring(Page.Url.LastIndexOf('/'));
[TestMethod]
public async Task LastCreatedIssueShouldBeOnTheServer()
{
await Page.GotoAsync("https://github.com/" + USER + "/" + REPO + "/issues");
await Page.Locator("text=New Issue").ClickAsync();
await Page.Locator("[aria-label='Title']").FillAsync("Bug report 1");
await Page.Locator("[aria-label='Comment body']").FillAsync("Bug description");
await Page.Locator("text=Submit new issue").ClickAsync();
var issueId = Page.Url.Substring(Page.Url.LastIndexOf('/'));
var newIssue = await Request.GetAsync("https://github.com/" + USER + "/" + REPO + "/issues/" + issueId);
Assert.True(newIssue.Ok);
StringAssert.Contains(await newIssue.TextAsync(), "Bug report 1");
}
var newIssue = await Request.GetAsync("https://github.com/" + USER + "/" + REPO + "/issues/" + issueId);
await Expect(newIssue).ToBeOKAsync();
StringAssert.Contains(await newIssue.TextAsync(), "Bug report 1");
}
}
```

View File

@ -47,21 +47,19 @@ def test_status_becomes_submitted(page: Page) -> None:
```
```csharp
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Playwright.NUnit;
using NUnit.Framework;
using Microsoft.Playwright;
using Microsoft.Playwright.MSTest;
namespace PlaywrightTests;
[TestFixture]
[TestClass]
public class ExampleTests : PageTest
{
[Test]
[TestMethod]
public async Task StatusBecomesSubmitted()
{
// ..
await Page.GetByRole(AriaRole.Button).ClickAsync();
// ...
await Page.GetByRole(AriaRole.Button, new() { Name = "Sign In" }).ClickAsync();
await Expect(Page.Locator(".status")).ToHaveTextAsync("Submitted");
}
}

View File

@ -50,21 +50,19 @@ def test_navigates_to_login_page(page: Page) -> None:
```csharp
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Playwright.NUnit;
using NUnit.Framework;
using Microsoft.Playwright;
using Microsoft.Playwright.MSTest;
namespace PlaywrightTests;
[TestFixture]
[TestClass]
public class ExampleTests : PageTest
{
[Test]
[TestMethod]
public async Task NavigatetoLoginPage()
{
// ..
await Page.GetByText("Sing in").ClickAsync();
await Expect(Page.Locator("div#foobar")).ToHaveURL(new Regex(".*/login"));
await Page.GetByRole(AriaRole.Button, new() { Name = "Sign In" }).ClickAsync();
await Expect(Page).ToHaveURLAsync(new Regex(".*/login"));
}
}
```

View File

@ -50,19 +50,18 @@ public class TestExample {
```
```csharp
using System.Threading.Tasks;
using Microsoft.Playwright.NUnit;
using NUnit.Framework;
using Microsoft.Playwright;
using Microsoft.Playwright.MSTest;
namespace PlaywrightTests;
[TestFixture]
[TestClass]
public class ExampleTests : PageTest
{
[Test]
[TestMethod]
public async Task StatusBecomesSubmitted()
{
await Page.Locator("#submit-button").ClickAsync();
await Page.GetByRole(AriaRole.Button, new() { Name = "Submit" }).ClickAsync();
await Expect(Page.Locator(".status")).ToHaveTextAsync("Submitted");
}
}

View File

@ -7,16 +7,16 @@ title: "Installation"
Playwright was created specifically to accommodate the needs of end-to-end testing. Playwright supports all modern rendering engines including Chromium, WebKit, and Firefox. Test on Windows, Linux, and macOS, locally or on CI, headless or headed with native mobile emulation.
You can choose to use [NUnit base classes](./test-runners.md#nunit) or [MSTest base classes](./test-runners.md#mstest) that Playwright provides to write end-to-end tests. These classes support running tests on multiple browser engines, parallelizing tests, adjusting launch/context options and getting a [Page]/[BrowserContext] instance per test out of the box. Alternatively you can use the [library](./library.md) to manually write the testing infrastructure.
You can choose to use [MSTest base classes](./test-runners.md#mstest) or [NUnit base classes](./test-runners.md#nunit) that Playwright provides to write end-to-end tests. These classes support running tests on multiple browser engines, parallelizing tests, adjusting launch/context options and getting a [Page]/[BrowserContext] instance per test out of the box. Alternatively you can use the [library](./library.md) to manually write the testing infrastructure.
1. Start by creating a new project with `dotnet new`. This will create the `PlaywrightTests` directory which includes a `UnitTest1.cs` file:
<Tabs
groupId="test-runners"
defaultValue="nunit"
defaultValue="mstest"
values={[
{label: 'MSTest', value: 'mstest'},
{label: 'NUnit', value: 'nunit'},
{label: 'MSTest', value: 'mstest'}
]
}>
<TabItem value="nunit">
@ -41,10 +41,10 @@ cd PlaywrightTests
<Tabs
groupId="test-runners"
defaultValue="nunit"
defaultValue="mstest"
values={[
{label: 'MSTest', value: 'mstest'},
{label: 'NUnit', value: 'nunit'},
{label: 'MSTest', value: 'mstest'}
]
}>
<TabItem value="nunit">
@ -83,10 +83,10 @@ Edit the `UnitTest1.cs` file with the code below to create an example end-to-end
<Tabs
groupId="test-runners"
defaultValue="nunit"
defaultValue="mstest"
values={[
{label: 'MSTest', value: 'mstest'},
{label: 'NUnit', value: 'nunit'},
{label: 'MSTest', value: 'mstest'}
]
}>
<TabItem value="nunit">
@ -132,10 +132,8 @@ public class ExampleTest : PageTest
```csharp title="UnitTest1.cs"
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Playwright;
using Microsoft.Playwright.MSTest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace PlaywrightTests;
@ -192,4 +190,4 @@ See our doc on [Running and Debugging Tests](./running-tests.md) to learn more a
- [Generate tests with Codegen](./codegen-intro.md)
- [See a trace of your tests](./trace-viewer-intro.md)
- [Run tests on CI](./ci-intro.md)
- [Learn more about the NUnit and MSTest base classes](./test-runners.md)
- [Learn more about the MSTest and NUnit base classes](./test-runners.md)

View File

@ -30,7 +30,7 @@ You can choose any testing framework such as JUnit or TestNG based on your proje
## .NET
Playwright for .NET comes with [NUnit base classes](https://playwright.dev/dotnet/docs/test-runners#nunit) and [MSTest base classes](https://playwright.dev/dotnet/docs/test-runners#mstest) for writing end-to-end tests.
Playwright for .NET comes with [MSTest base classes](https://playwright.dev/dotnet/docs/test-runners#mstest) and [NUnit base classes](https://playwright.dev/dotnet/docs/test-runners#nunit) for writing end-to-end tests.
* [Documentation](https://playwright.dev/dotnet/docs/intro)
* [GitHub repo](https://github.com/microsoft/playwright-dotnet)

View File

@ -5,7 +5,7 @@ title: "Getting started - Library"
## Introduction
Playwright can either be used with the [NUnit](./test-runners.md#nunit) or [MSTest](./test-runners.md#mstest), or as a Playwright Library (this guide). If you are working on an application that utilizes Playwright capabilities or you are using Playwright with another test runner, read on.
Playwright can either be used with the [MSTest](./test-runners.md#mstest) or [NUnit](./test-runners.md#nunit), or as a Playwright Library (this guide). If you are working on an application that utilizes Playwright capabilities or you are using Playwright with another test runner, read on.
## Usage

View File

@ -637,7 +637,7 @@ This version was also tested against the following stable channels:
### Other highlights
- New option `MaxRedirects` for [`method: APIRequestContext.get`] and others to limit redirect count.
- Codegen now supports NUnit and MSTest frameworks.
- Codegen now supports MSTest and NUnit frameworks.
- ASP .NET is now supported.
### Behavior Change

View File

@ -109,10 +109,10 @@ dotnet test --filter "Name~GetStartedLink"
<Tabs
groupId="test-runners"
defaultValue="nunit"
defaultValue="mstest"
values={[
{label: 'MSTest', value: 'mstest'},
{label: 'NUnit', value: 'nunit'},
{label: 'MSTest', value: 'mstest'}
]
}>
<TabItem value="nunit">
@ -159,4 +159,4 @@ Check out our [debugging guide](./debug.md) to learn more about the [Playwright
- [Generate tests with Codegen](./codegen-intro.md)
- [See a trace of your tests](./trace-viewer-intro.md)
- [Run tests on CI](./ci-intro.md)
- [Learn more about the NUnit and MSTest base classes](./test-runners.md)
- [Learn more about the MSTest and NUnit base classes](./test-runners.md)

View File

@ -77,10 +77,10 @@ expect.set_options(timeout=10_000)
<Tabs
groupId="test-runners"
defaultValue="nunit"
defaultValue="mstest"
values={[
{label: 'MSTest', value: 'mstest'},
{label: 'NUnit', value: 'nunit'},
{label: 'MSTest', value: 'mstest'}
]
}>
<TabItem value="nunit">

View File

@ -5,135 +5,12 @@ title: "Test Runners"
## Introduction
While Playwright for .NET isn't tied to a particular test runner or testing framework, in our experience it works best with the built-in .NET test runner, and using NUnit as the test framework. NUnit is also what we use internally for [our tests](https://github.com/microsoft/playwright-dotnet/tree/main/src/Playwright.Tests).
While Playwright for .NET isn't tied to a particular test runner or testing framework, in our experience the easiest way of getting started is by using the base classes we provide for [MSTest](#mstest) and [NUnit](#nunit). These classes support running tests on multiple browser engines, adjusting launch/context options and getting a [Page]/[BrowserContext] instance per test out of the box.
Playwright and Browser instances can be reused between tests for better performance. We
Playwright and Browser instances will be reused between tests for better performance. We
recommend running each test case in a new BrowserContext, this way browser state will be
isolated between the tests.
## NUnit
Playwright provides base classes to write tests with NUnit via the [`Microsoft.Playwright.NUnit`](https://www.nuget.org/packages/Microsoft.Playwright.NUnit) package.
Check out the [installation guide](./intro.md) to get started.
### Running NUnit tests in Parallel
By default NUnit will run all test files in parallel, while running tests inside each file sequentially (`ParallelScope.Self`). It will create as many processes as there are cores on the host system. You can adjust this behavior using the NUnit.NumberOfTestWorkers parameter.
Only `ParallelScope.Self` is supported.
For CPU-bound tests, we recommend using as many workers as there are cores on your system, divided by 2. For IO-bound tests you can use as many workers as you have cores.
```bash
dotnet test -- NUnit.NumberOfTestWorkers=5
```
### Customizing [BrowserContext] options
To customize context options, you can override the `ContextOptions` method of your test class derived from `Microsoft.Playwright.MSTest.PageTest` or `Microsoft.Playwright.MSTest.ContextTest`. See the following example:
```csharp
using Microsoft.Playwright.NUnit;
namespace PlaywrightTests;
[Parallelizable(ParallelScope.Self)]
[TestFixture]
public class MyTest : PageTest
{
[Test]
public async Task TestWithCustomContextOptions()
{
// The following Page (and BrowserContext) instance has the custom colorScheme, viewport and baseURL set:
await Page.GotoAsync("/login");
}
public override BrowserNewContextOptions ContextOptions()
{
return new BrowserNewContextOptions()
{
ColorScheme = ColorScheme.Light,
ViewportSize = new()
{
Width = 1920,
Height = 1080
},
BaseURL = "https://github.com",
};
}
}
```
### Customizing [Browser]/launch options
[Browser]/launch options can be overridden either using a run settings file or by setting the run settings options directly via the
CLI. See the following example:
```xml
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<Playwright>
<BrowserName>chromium</BrowserName>
<LaunchOptions>
<Headless>false</Headless>
<Channel>msedge</Channel>
</LaunchOptions>
</Playwright>
</RunSettings>
```
```bash
dotnet test -- Playwright.BrowserName=chromium Playwright.LaunchOptions.Headless=false Playwright.LaunchOptions.Channel=msedge
```
### Using Verbose API Logs
When you have enabled the [verbose API log](./debug.md#verbose-api-logs), via the `DEBUG` environment variable, you will see the messages in the standard error stream. In NUnit, within Visual Studio, that will be the `Tests` pane of the `Output` window. It will also be displayed in the `Test Log` for each test.
### Using the .runsettings file
When running tests from Visual Studio, you can take advantage of the `.runsettings` file. The following shows a reference of the supported values.
For example, to specify the amount of workers you can use `NUnit.NumberOfTestWorkers` or to enable `DEBUG` logs `RunConfiguration.EnvironmentVariables`.
```xml
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<!-- NUnit adapter -->
<NUnit>
<NumberOfTestWorkers>24</NumberOfTestWorkers>
</NUnit>
<!-- General run configuration -->
<RunConfiguration>
<EnvironmentVariables>
<!-- For debugging selectors, it's recommend to set the following environment variable -->
<DEBUG>pw:api</DEBUG>
</EnvironmentVariables>
</RunConfiguration>
<!-- Playwright -->
<Playwright>
<BrowserName>chromium</BrowserName>
<ExpectTimeout>5000</ExpectTimeout>
<LaunchOptions>
<Headless>false</Headless>
<Channel>msedge</Channel>
</LaunchOptions>
</Playwright>
</RunSettings>
```
### Base NUnit classes for Playwright
There are a few base classes available to you in `Microsoft.Playwright.NUnit` namespace:
|Test |Description|
|--------------|-----------|
|PageTest |Each test gets a fresh copy of a web [Page] created in its own unique [BrowserContext]. Extending this class is the simplest way of writing a fully-functional Playwright test.<br></br><br></br>Note: You can override the `ContextOptions` method in each test file to control context options, the ones typically passed into the [`method: Browser.newContext`] method. That way you can specify all kinds of emulation options for your test file individually.|
|ContextTest |Each test will get a fresh copy of a [BrowserContext]. You can create as many pages in this context as you'd like. Using this test is the easiest way to test multi-page scenarios where you need more than one tab.<br></br><br></br>Note: You can override the `ContextOptions` method in each test file to control context options, the ones typically passed into the [`method: Browser.newContext`] method. That way you can specify all kinds of emulation options for your test file individually.|
|BrowserTest |Each test will get a browser and can create as many contexts as it likes. Each test is responsible for cleaning up all the contexts it created.|
|PlaywrightTest|This gives each test a Playwright object so that the test could start and stop as many browsers as it likes.|
## MSTest
Playwright provides base classes to write tests with MSTest via the [`Microsoft.Playwright.MSTest`](https://www.nuget.org/packages/Microsoft.Playwright.MSTest) package.
@ -259,6 +136,128 @@ There are a few base classes available to you in `Microsoft.Playwright.MSTest` n
|BrowserTest |Each test will get a browser and can create as many contexts as it likes. Each test is responsible for cleaning up all the contexts it created.|
|PlaywrightTest|This gives each test a Playwright object so that the test could start and stop as many browsers as it likes.|
## NUnit
Playwright provides base classes to write tests with NUnit via the [`Microsoft.Playwright.NUnit`](https://www.nuget.org/packages/Microsoft.Playwright.NUnit) package.
Check out the [installation guide](./intro.md) to get started.
### Running NUnit tests in Parallel
By default NUnit will run all test files in parallel, while running tests inside each file sequentially (`ParallelScope.Self`). It will create as many processes as there are cores on the host system. You can adjust this behavior using the NUnit.NumberOfTestWorkers parameter.
Only `ParallelScope.Self` is supported.
For CPU-bound tests, we recommend using as many workers as there are cores on your system, divided by 2. For IO-bound tests you can use as many workers as you have cores.
```bash
dotnet test -- NUnit.NumberOfTestWorkers=5
```
### Customizing [BrowserContext] options
To customize context options, you can override the `ContextOptions` method of your test class derived from `Microsoft.Playwright.MSTest.PageTest` or `Microsoft.Playwright.MSTest.ContextTest`. See the following example:
```csharp
using Microsoft.Playwright.NUnit;
namespace PlaywrightTests;
[Parallelizable(ParallelScope.Self)]
[TestFixture]
public class MyTest : PageTest
{
[Test]
public async Task TestWithCustomContextOptions()
{
// The following Page (and BrowserContext) instance has the custom colorScheme, viewport and baseURL set:
await Page.GotoAsync("/login");
}
public override BrowserNewContextOptions ContextOptions()
{
return new BrowserNewContextOptions()
{
ColorScheme = ColorScheme.Light,
ViewportSize = new()
{
Width = 1920,
Height = 1080
},
BaseURL = "https://github.com",
};
}
}
```
### Customizing [Browser]/launch options
[Browser]/launch options can be overridden either using a run settings file or by setting the run settings options directly via the
CLI. See the following example:
```xml
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<Playwright>
<BrowserName>chromium</BrowserName>
<LaunchOptions>
<Headless>false</Headless>
<Channel>msedge</Channel>
</LaunchOptions>
</Playwright>
</RunSettings>
```
```bash
dotnet test -- Playwright.BrowserName=chromium Playwright.LaunchOptions.Headless=false Playwright.LaunchOptions.Channel=msedge
```
### Using Verbose API Logs
When you have enabled the [verbose API log](./debug.md#verbose-api-logs), via the `DEBUG` environment variable, you will see the messages in the standard error stream. In NUnit, within Visual Studio, that will be the `Tests` pane of the `Output` window. It will also be displayed in the `Test Log` for each test.
### Using the .runsettings file
When running tests from Visual Studio, you can take advantage of the `.runsettings` file. The following shows a reference of the supported values.
For example, to specify the amount of workers you can use `NUnit.NumberOfTestWorkers` or to enable `DEBUG` logs `RunConfiguration.EnvironmentVariables`.
```xml
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<!-- NUnit adapter -->
<NUnit>
<NumberOfTestWorkers>24</NumberOfTestWorkers>
</NUnit>
<!-- General run configuration -->
<RunConfiguration>
<EnvironmentVariables>
<!-- For debugging selectors, it's recommend to set the following environment variable -->
<DEBUG>pw:api</DEBUG>
</EnvironmentVariables>
</RunConfiguration>
<!-- Playwright -->
<Playwright>
<BrowserName>chromium</BrowserName>
<ExpectTimeout>5000</ExpectTimeout>
<LaunchOptions>
<Headless>false</Headless>
<Channel>msedge</Channel>
</LaunchOptions>
</Playwright>
</RunSettings>
```
### Base NUnit classes for Playwright
There are a few base classes available to you in `Microsoft.Playwright.NUnit` namespace:
|Test |Description|
|--------------|-----------|
|PageTest |Each test gets a fresh copy of a web [Page] created in its own unique [BrowserContext]. Extending this class is the simplest way of writing a fully-functional Playwright test.<br></br><br></br>Note: You can override the `ContextOptions` method in each test file to control context options, the ones typically passed into the [`method: Browser.newContext`] method. That way you can specify all kinds of emulation options for your test file individually.|
|ContextTest |Each test will get a fresh copy of a [BrowserContext]. You can create as many pages in this context as you'd like. Using this test is the easiest way to test multi-page scenarios where you need more than one tab.<br></br><br></br>Note: You can override the `ContextOptions` method in each test file to control context options, the ones typically passed into the [`method: Browser.newContext`] method. That way you can specify all kinds of emulation options for your test file individually.|
|BrowserTest |Each test will get a browser and can create as many contexts as it likes. Each test is responsible for cleaning up all the contexts it created.|
|PlaywrightTest|This gives each test a Playwright object so that the test could start and stop as many browsers as it likes.|
## xUnit support
While using xUnit is also supported, we do not support running parallel tests. This is a well known problem/design limitation

View File

@ -18,10 +18,10 @@ Traces can be recorded using the [`property: BrowserContext.tracing`] API as fol
<Tabs
groupId="test-runners"
defaultValue="nunit"
defaultValue="mstest"
values={[
{label: 'MSTest', value: 'mstest'},
{label: 'NUnit', value: 'nunit'},
{label: 'MSTest', value: 'mstest'}
]
}>
<TabItem value="nunit">
@ -134,4 +134,4 @@ Check out our detailed guide on [Trace Viewer](/trace-viewer.md) to learn more a
## What's next
- [Run tests on CI with GitHub Actions](/ci-intro.md)
- [Learn more about the NUnit and MSTest base classes](./test-runners.md)
- [Learn more about the MSTest and NUnit base classes](./test-runners.md)

View File

@ -244,10 +244,10 @@ Traces can be recorded using the [`property: BrowserContext.tracing`] API as fol
<Tabs
groupId="test-runners"
defaultValue="nunit"
defaultValue="mstest"
values={[
{label: 'MSTest', value: 'mstest'},
{label: 'NUnit', value: 'nunit'},
{label: 'MSTest', value: 'mstest'}
]
}>
<TabItem value="nunit">
@ -355,10 +355,10 @@ Setup your tests to record a trace only when the test fails:
<Tabs
groupId="test-runners"
defaultValue="nunit"
defaultValue="mstest"
values={[
{label: 'MSTest', value: 'mstest'},
{label: 'NUnit', value: 'nunit'},
{label: 'MSTest', value: 'mstest'}
]
}>
<TabItem value="nunit">

View File

@ -347,14 +347,14 @@ def test_webview2(page: Page):
```csharp
// WebView2Test.cs
using System.Text.RegularExpressions;
using Microsoft.Playwright.NUnit;
using Microsoft.Playwright;
using System.Diagnostics;
using Microsoft.Playwright;
using Microsoft.Playwright.MSTest;
namespace dotnet_nunit;
namespace PlaywrightTests;
public class WebView2Test : PlaywrightTest
[TestClass]
public class ExampleTest : PlaywrightTest
{
public IBrowser Browser { get; internal set; } = null!;
public IBrowserContext Context { get; internal set; } = null!;
@ -363,12 +363,12 @@ public class WebView2Test : PlaywrightTest
private string _userDataDir = null!;
private string _executablePath = Path.Join(Directory.GetCurrentDirectory(), @"..\..\..\..\webview2-app\bin\Debug\net8.0-windows\webview2.exe");
[SetUp]
public async Task BrowserSetUp()
[TestInitialize]
public async Task BrowserTestInitialize()
{
var cdpPort = 10000 + WorkerIndex;
Assert.IsTrue(File.Exists(_executablePath), "Make sure that the executable exists");
_userDataDir = Path.Join(Path.GetTempPath(), $"playwright-webview2-tests/user-data-dir-{TestContext.CurrentContext.WorkerId}");
_userDataDir = Path.Join(Path.GetTempPath(), $"playwright-webview2-tests/user-data-dir-{WorkerIndex}");
// WebView2 does some lazy cleanups on shutdown so we can't clean it up after each test
if (Directory.Exists(_userDataDir))
{
@ -401,8 +401,8 @@ public class WebView2Test : PlaywrightTest
Page = Context.Pages[0];
}
[TearDown]
public async Task BrowserTearDown()
[TestCleanup]
public async Task BrowserTestCleanup()
{
_webView2Process!.Kill(true);
await Browser.CloseAsync();
@ -412,14 +412,15 @@ public class WebView2Test : PlaywrightTest
```csharp
// UnitTest1.cs
using Microsoft.Playwright.NUnit;
using Microsoft.Playwright;
using Microsoft.Playwright.MSTest;
namespace dotnet_nunit;
namespace PlaywrightTests;
[Parallelizable(ParallelScope.Self)]
public class Tests : WebView2Test
[TestClass]
public class ExampleTest : WebView2Test
{
[Test]
[TestMethod]
public async Task HomepageHasPlaywrightInTitleAndGetStartedLinkLinkingtoTheIntroPage()
{
await Page.GotoAsync("https://playwright.dev");

View File

@ -35,10 +35,10 @@ Take a look at the following example to see how to write a test.
<Tabs
groupId="test-runners"
defaultValue="nunit"
defaultValue="mstest"
values={[
{label: 'MSTest', value: 'mstest'},
{label: 'NUnit', value: 'nunit'},
{label: 'MSTest', value: 'mstest'}
]
}>
<TabItem value="nunit">
@ -200,10 +200,10 @@ The Playwright NUnit and MSTest test framework base classes will isolate each te
<Tabs
groupId="test-runners"
defaultValue="nunit"
defaultValue="mstest"
values={[
{label: 'MSTest', value: 'mstest'},
{label: 'NUnit', value: 'nunit'},
{label: 'MSTest', value: 'mstest'}
]
}>
<TabItem value="nunit">
@ -257,10 +257,10 @@ You can use `SetUp`/`TearDown` in NUnit or `TestInitialize`/`TestCleanup` in MST
<Tabs
groupId="test-runners"
defaultValue="nunit"
defaultValue="mstest"
values={[
{label: 'MSTest', value: 'mstest'},
{label: 'NUnit', value: 'nunit'},
{label: 'MSTest', value: 'mstest'}
]
}>
<TabItem value="nunit">
@ -328,4 +328,4 @@ public class ExampleTest : PageTest
- [Generate tests with Codegen](./codegen-intro.md)
- [See a trace of your tests](./trace-viewer-intro.md)
- [Run tests on CI](./ci-intro.md)
- [Learn more about the NUnit and MSTest base classes](./test-runners.md)
- [Learn more about the MSTest and NUnit base classes](./test-runners.md)