Repository: dotnet/AspNetCore.Docs
Pull request #35328: Integration tests documentation add nunit mstest
Author: login: jonorogers
Created: 2025-04-29T23:14:05Z | Merged: 2025-05-15T18:50:04Z

This PR adds support for xUnit & NUnit into the integration tests documentation; including snippets for all three popular testing libraries (xUnit, MSTest, NUnit). This PR follows the examples of already exisiting MS documentation i.e. https://learn.microsoft.com/en-us/dotnet/aspire/testing/write-your-first-test?pivots=mstest

I'm submitting a PR as suggested by @tdykstra here: https://github.com/dotnet/AspNetCore.Docs/issues/35290#issuecomment-2822837077

Notes:

- I haven't updated the sample project in AspNetCore.Docs.Samples - this could be useful to add another two testing projects, but I wasn't sure what the scope of this PR should be.
- Some of the article text still references xUnit; as it's describing the samples project. I'm not sure if this is now confusing, as the pivot groups aren't referencing xUnit if it's not selected.
- In MSTest I personally favour using `[AssemblyInitialize]` rather than `[ClassInitialize]` when instantiating a `WebApplicationFactory` in integration tests, but I opted for equivalence between the three examples.
- I noticed that the samples are split up by .NET versions, but I have opted instead to follow the example of the Aspire docs linked above and just provide snippets in C# for the three unit testing frameworks.
- I have no idea if there is any other code which needs to be modified to support the additional pivot group declaration.

Fixes #35290



<!--
# Instructions

When creating a new PR, please reference the issue number if there is one:

Fixes #Issue_Number

The "Fixes #nnn" syntax in the PR description allows GitHub to automatically close the issue when this PR is merged.

NOTE: This is a comment; please type your descriptions above or below it.
-->

<!-- PREVIEW-TABLE-START -->

---

#### Internal previews

| 📄 File | 🔗 Preview link |
|:--|:--|
| [aspnetcore/test/integration-tests.md](https://github.com/dotnet/AspNetCore.Docs/blob/7224bd94497860af9bf28cca3626b527904de2ab/aspnetcore/test/integration-tests.md) | [Integration tests in ASP.NET Core](https://review.learn.microsoft.com/en-us/aspnet/core/test/integration-tests?branch=pr-en-us-35328) |
| [aspnetcore/zone-pivot-groups.yml](https://github.com/dotnet/AspNetCore.Docs/blob/7224bd94497860af9bf28cca3626b527904de2ab/aspnetcore/zone-pivot-groups.yml) | [aspnetcore/zone-pivot-groups](https://review.learn.microsoft.com/en-us/aspnet/core/zone-pivot-groups?branch=pr-en-us-35328) |


<!-- PREVIEW-TABLE-END -->

Linked issue #35290: Add MSTest/NUnit options
### Description

This page assumes the use of xUnit. It would be good if it gave examples in MSTest/NUnit like other documentation pages i.e.: https://learn.microsoft.com/en-us/dotnet/aspire/testing/write-your-first-test?pivots=mstest

### Page URL

https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-8.0

### Content source URL

https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/test/integration-tests.md

### Document ID

f499b131-8b14-7e10-1728-543fdd5a5656

### Platform Id

a725fe2a-b11f-6664-eb77-2adc94d55792

### Article author

@tdykstra

### Metadata

* ID: 77889d43-fedb-f8b4-8fea-379ec946555c
* PlatformId: a725fe2a-b11f-6664-eb77-2adc94d55792 
* Service: **aspnet-core**
* Sub-service: **testing**

[Related Issues](https://github.com/dotnet/AspNetCore.Docs/issues?q=is%3Aissue+is%3Aopen+f499b131-8b14-7e10-1728-543fdd5a5656)
Linked issue comments: user: login: tdykstra
created_at: 2025-04-23T01:29:38Z
body: Yes, it would be good to have that additional code in the article However, given the size this article and the amount of sample code in it, we don't have the resources to add  MSTest/NUnit. If you would be interested in submitting a PR to add the code we'd be happy to help you get it published.
author_association: CONTRIBUTOR
user: login: jonorogers
created_at: 2025-04-30T23:14:35Z
body: Hey @tdykstra I created a PR to address this; https://github.com/dotnet/AspNetCore.Docs/pull/35328
author_association: CONTRIBUTOR

Diff:
diff --git a/aspnetcore/test/integration-tests.md b/aspnetcore/test/integration-tests.md
index b4ffeb1bc0e7..dfcb93ff9821 100644
--- a/aspnetcore/test/integration-tests.md
+++ b/aspnetcore/test/integration-tests.md
@@ -7,6 +7,7 @@ ms.author: tdykstra
 ms.custom: mvc
 ms.date: 3/24/2025
 uid: test/integration-tests
+zone_pivot_groups: unit-testing-framework
 ---
 # Integration tests in ASP.NET Core
 
@@ -63,7 +64,21 @@ The following test class, `BasicTests`, uses the `WebApplicationFactory` to boot
 
 <xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory%601.CreateClient> creates an instance of `HttpClient` that automatically follows redirects and handles cookies.
   
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/10.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/BasicTests.cs?name=snippet1)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/BasicTests.cs" id="snippet1":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/BasicTests.cs" id="snippet1":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/BasicTests.cs" id="snippet1":::
+
+:::zone-end
 
 By default, non-essential cookies aren't preserved across requests when the [General Data Protection Regulation consent policy](xref:security/gdpr) is enabled. To preserve non-essential cookies, such as those used by the TempData provider, mark them as essential in your tests. For instructions on marking a cookie as essential, see [Essential cookies](xref:security/gdpr#essential-cookies).
 
@@ -79,7 +94,21 @@ Web host configuration can be created independently of the test classes by inher
 
 1. Inherit from `WebApplicationFactory` and override <xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory%601.ConfigureWebHost%2A>. The <xref:Microsoft.AspNetCore.Hosting.IWebHostBuilder> allows the configuration of the service collection with [`IWebHostBuilder.ConfigureServices`](xref:Microsoft.AspNetCore.Hosting.IWebHostBuilder.ConfigureServices%2A)
 
-   [!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/10.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/CustomWebApplicationFactory.cs?name=snippet1)]
+   :::zone pivot="xunit"
+
+   :::code language="csharp" source="~/test/integration-tests/snippets/xunit/CustomWebApplicationFactory.cs" id="snippet1":::
+
+   :::zone-end
+   :::zone pivot="mstest"
+
+   :::code language="csharp" source="~/test/integration-tests/snippets/mstest/CustomWebApplicationFactory.cs" id="snippet1":::
+
+   :::zone-end
+   :::zone pivot="nunit"
+
+   :::code language="csharp" source="~/test/integration-tests/snippets/nunit/CustomWebApplicationFactory.cs" id="snippet1":::
+
+   :::zone-end
 
    Database seeding in the [sample app](https://github.com/dotnet/AspNetCore.Docs.Samples/tree/main/test/integration-tests/10.x/IntegrationTestsSample) is performed by the `InitializeDbForTests` method. The method is described in the [Integration tests sample: Test app organization](#test-app-organization) section.
 
@@ -96,13 +125,41 @@ Web host configuration can be created independently of the test classes by inher
 -->
 2. Use the custom `CustomWebApplicationFactory` in test classes. The following example uses the factory in the `IndexPageTests` class:
 
-   [!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/10.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/IndexPageTests.cs?name=snippet1)]
+   :::zone pivot="xunit"
+
+   :::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs" id="snippet1":::
+
+   :::zone-end
+   :::zone pivot="mstest"
+
+   :::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs" id="snippet1":::
+
+   :::zone-end
+   :::zone pivot="nunit"
+
+   :::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs" id="snippet1":::
+
+   :::zone-end
 
    The sample app's client is configured to prevent the `HttpClient` from following redirects. As explained later in the [Mock authentication](#mock-authentication) section, this permits tests to check the result of the app's first response. The first response is a redirect in many of these tests with a `Location` header.
 
 3. A typical test uses the `HttpClient` and helper methods to process the request and the response:
 
-   [!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/10.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/IndexPageTests.cs?name=snippet2)]
+   :::zone pivot="xunit"
+
+   :::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs" id="snippet2":::
+
+   :::zone-end
+   :::zone pivot="mstest"
+
+   :::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs" id="snippet2":::
+
+   :::zone-end
+   :::zone pivot="nunit"
+
+   :::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs" id="snippet2":::
+
+   :::zone-end
 
 Any POST request to the SUT must satisfy the antiforgery check that's automatically made by the app's [data protection antiforgery system](xref:security/data-protection/introduction). In order to arrange for a test's POST request, the test app must:
 
@@ -135,7 +192,21 @@ The `Post_DeleteMessageHandler_ReturnsRedirectToRoot` test method of the [sample
 
 Because another test in the `IndexPageTests` class performs an operation that deletes all of the records in the database and may run before the `Post_DeleteMessageHandler_ReturnsRedirectToRoot` method, the database is reseeded in this test method to ensure that a record is present for the SUT to delete. Selecting the first delete button of the `messages` form in the SUT is simulated in the request to the SUT:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/10.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/IndexPageTests.cs?name=snippet3)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs" id="snippet3":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs" id="snippet3":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs" id="snippet3":::
+
+:::zone-end
 
 ## Client options
 
@@ -143,9 +214,23 @@ See the <xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOption
 
 Create the `WebApplicationFactoryClientOptions` class and pass it to the <xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory%601.CreateClient> method:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/10.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/IndexPageTests.cs?name=snippet1)]
+:::zone pivot="xunit"
 
-***NOTE:*** To avoid HTTPS redirection warnings in logs when using HTTPS Redirection Middleware, set `BaseAddress = new Uri("https://localhost")`
+:::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs" id="snippet1":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs" id="snippet1":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs" id="snippet1":::
+
+:::zone-end
+
+**_NOTE:_** To avoid HTTPS redirection warnings in logs when using HTTPS Redirection Middleware, set `BaseAddress = new Uri("https://localhost")`
 
 ## Inject mock services
 
@@ -188,11 +273,39 @@ To test the service and quote injection in an integration test, a mock service i
 
 `IntegrationTests.IndexPageTests.cs`:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/10.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/IndexPageTests.cs?name=snippet4)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs" id="snippet4":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs" id="snippet4":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs" id="snippet4":::
+
+:::zone-end
 
 `ConfigureTestServices` is called, and the scoped service is registered:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/10.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/IndexPageTests.cs?name=snippet5&highlight=7-10,17,20-21)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs" id="snippet5" highlight="7-10,17,20-21":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs" id="snippet5" highlight="7-10,17,20-21":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs" id="snippet5" highlight="7-10,17,20-22":::
+
+:::zone-end
 
 The markup produced during the test's execution reflects the quote text supplied by `TestQuoteService`, thus the assertion passes:
 
@@ -214,7 +327,21 @@ In the SUT, the `/SecurePage` page uses an <xref:Microsoft.Extensions.Dependency
 
 In the `Get_SecurePageRedirectsAnUnauthenticatedUser` test, a <xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions> is set to disallow redirects by setting <xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions.AllowAutoRedirect> to `false`:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/10.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/AuthTests.cs?name=snippet2)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/AuthTests.cs" id="snippet2":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/AuthTests.cs" id="snippet2":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/AuthTests.cs" id="snippet2":::
+
+:::zone-end
 
 By disallowing the client to follow the redirect, the following checks can be made:
 
@@ -223,11 +350,39 @@ By disallowing the client to follow the redirect, the following checks can be ma
 
 The test app can mock an <xref:Microsoft.AspNetCore.Authentication.AuthenticationHandler%601> in <xref:Microsoft.AspNetCore.TestHost.WebHostBuilderExtensions.ConfigureTestServices%2A> in order to test aspects of authentication and authorization. A minimal scenario returns an <xref:Microsoft.AspNetCore.Authentication.AuthenticateResult.Success%2A?displayProperty=nameWithType>:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/10.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/AuthTests.cs?name=snippet4&highlight=11-18)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/AuthTests.cs" id="snippet4" highlight="11-18":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/AuthTests.cs" id="snippet4" highlight="11-18":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/AuthTests.cs" id="snippet4" highlight="11-18":::
+
+:::zone-end
 
 The `TestAuthHandler` is called to authenticate a user when the authentication scheme is set to `TestScheme` where `AddAuthentication` is registered for `ConfigureTestServices`. It's important for the `TestScheme` scheme to match the scheme your app expects. Otherwise, authentication won't work.
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/10.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/AuthTests.cs?name=snippet3&highlight=7-12)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/AuthTests.cs" id="snippet3" highlight="7-12":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/AuthTests.cs" id="snippet3" highlight="7-12":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/AuthTests.cs" id="snippet3" highlight="7-12":::
+
+:::zone-end
 
 For more information on `WebApplicationFactoryClientOptions`, see the [Client options](#client-options) section.
 
@@ -239,7 +394,21 @@ See [this GitHub repository](https://github.com/blowdart/idunno.Authentication/t
 
 Set the [environment](xref:fundamentals/environments) in the custom application factory:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/10.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/CustomWebApplicationFactory.cs?name=snippet1&highlight=36)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/xunit/CustomWebApplicationFactory.cs" id="snippet1" highlight="36":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/mstest/CustomWebApplicationFactory.cs" id="snippet1" highlight="36":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/nunit/CustomWebApplicationFactory.cs" id="snippet1" highlight="36":::
+
+:::zone-end
 
 ## How the test infrastructure infers the app content root path
 
@@ -259,8 +428,22 @@ To disable shadow copying when using xUnit, create a `xunit.runner.json` file in
 
 ## Disposal of objects
 
+:::zone pivot="xunit"
+
 After the tests of the `IClassFixture` implementation are executed, <xref:Microsoft.AspNetCore.TestHost.TestServer> and <xref:System.Net.Http.HttpClient> are disposed when xUnit disposes of the [`WebApplicationFactory`](xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory%601). If objects instantiated by the developer require disposal, dispose of them in the `IClassFixture` implementation. For more information, see [Implementing a Dispose method](/dotnet/standard/garbage-collection/implementing-dispose).
 
+:::zone-end
+:::zone pivot="mstest"
+
+After the tests of the `TestClass` are executed, <xref:Microsoft.AspNetCore.TestHost.TestServer> and <xref:System.Net.Http.HttpClient> are disposed when MSTest disposes of the [`WebApplicationFactory`](xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory%601) in the `ClassCleanup` method. If objects instantiated by the developer require disposal, dispose of them in the `ClassCleanup` method. For more information, see [Implementing a Dispose method](/dotnet/standard/garbage-collection/implementing-dispose).
+
+:::zone-end
+:::zone pivot="nunit"
+
+After the tests of the test class are executed, <xref:Microsoft.AspNetCore.TestHost.TestServer> and <xref:System.Net.Http.HttpClient> are disposed when NUnit disposes of the [`WebApplicationFactory`](xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory%601) in the `TearDown` method. If objects instantiated by the developer require disposal, dispose of them in the `TearDown` method. For more information, see [Implementing a Dispose method](/dotnet/standard/garbage-collection/implementing-dispose).
+
+:::zone-end
+
 ## Integration tests sample
 
 The [sample app](https://github.com/dotnet/AspNetCore.Docs.Samples/tree/main/test/integration-tests/10.x/IntegrationTestsSample) is composed of two apps:
@@ -325,4 +508,4 @@ The SUT's database context is registered in `Program.cs`. The test app's `builde
 [!INCLUDE[](~/test/integration-tests/includes/integration-tests5.md)]
 [!INCLUDE[](~/test/integration-tests/includes/integration-tests7.md)]
 [!INCLUDE[](~/test/integration-tests/includes/integration-tests8.md)]
-[!INCLUDE[](~/test/integration-tests/includes/integration-tests9.md)]
\ No newline at end of file
+[!INCLUDE[](~/test/integration-tests/includes/integration-tests9.md)]
diff --git a/aspnetcore/test/integration-tests/includes/integration-tests9.md b/aspnetcore/test/integration-tests/includes/integration-tests9.md
index 59853681eb21..6e9bb363cf29 100644
--- a/aspnetcore/test/integration-tests/includes/integration-tests9.md
+++ b/aspnetcore/test/integration-tests/includes/integration-tests9.md
@@ -66,8 +66,22 @@ Test classes implement a *class fixture* interface ([`IClassFixture`](https://xu
 The following test class, `BasicTests`, uses the `WebApplicationFactory` to bootstrap the SUT and provide an <xref:System.Net.Http.HttpClient> to a test method, `Get_EndpointsReturnSuccessAndCorrectContentType`. The method verifies the response status code is successful (200-299) and the `Content-Type` header is `text/html; charset=utf-8` for several app pages.
 
 <xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory%601.CreateClient> creates an instance of `HttpClient` that automatically follows redirects and handles cookies.
-  
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/9.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/BasicTests.cs?name=snippet1)]
+
+:::zone pivot="xunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/BasicTests.cs" id="snippet1":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/BasicTests.cs" id="snippet1":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/BasicTests.cs" id="snippet1":::
+
+:::zone-end
 
 By default, non-essential cookies aren't preserved across requests when the [General Data Protection Regulation consent policy](xref:security/gdpr) is enabled. To preserve non-essential cookies, such as those used by the TempData provider, mark them as essential in your tests. For instructions on marking a cookie as essential, see [Essential cookies](xref:security/gdpr#essential-cookies).
 
@@ -83,7 +97,21 @@ Web host configuration can be created independently of the test classes by inher
 
 1. Inherit from `WebApplicationFactory` and override <xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory%601.ConfigureWebHost%2A>. The <xref:Microsoft.AspNetCore.Hosting.IWebHostBuilder> allows the configuration of the service collection with [`IWebHostBuilder.ConfigureServices`](xref:Microsoft.AspNetCore.Hosting.IWebHostBuilder.ConfigureServices%2A)
 
-   [!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/9.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/CustomWebApplicationFactory.cs?name=snippet1)]
+   :::zone pivot="xunit"
+
+   :::code language="csharp" source="~/test/integration-tests/snippets/xunit/CustomWebApplicationFactory.cs" id="snippet1":::
+
+   :::zone-end
+   :::zone pivot="mstest"
+
+   :::code language="csharp" source="~/test/integration-tests/snippets/mstest/CustomWebApplicationFactory.cs" id="snippet1":::
+
+   :::zone-end
+   :::zone pivot="nunit"
+
+   :::code language="csharp" source="~/test/integration-tests/snippets/nunit/CustomWebApplicationFactory.cs" id="snippet1":::
+
+   :::zone-end
 
    Database seeding in the [sample app](https://github.com/dotnet/AspNetCore.Docs.Samples/tree/main/test/integration-tests/9.x/IntegrationTestsSample) is performed by the `InitializeDbForTests` method. The method is described in the [Integration tests sample: Test app organization](#test-app-organization) section.
 
@@ -100,13 +128,41 @@ Web host configuration can be created independently of the test classes by inher
 -->
 2. Use the custom `CustomWebApplicationFactory` in test classes. The following example uses the factory in the `IndexPageTests` class:
 
-   [!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/9.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/IndexPageTests.cs?name=snippet1)]
+   :::zone pivot="xunit"
+
+   :::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs" id="snippet1":::
+
+   :::zone-end
+   :::zone pivot="mstest"
+
+   :::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs" id="snippet1":::
+
+   :::zone-end
+   :::zone pivot="nunit"
+
+   :::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs" id="snippet1":::
+
+   :::zone-end
 
    The sample app's client is configured to prevent the `HttpClient` from following redirects. As explained later in the [Mock authentication](#mock-authentication) section, this permits tests to check the result of the app's first response. The first response is a redirect in many of these tests with a `Location` header.
 
 3. A typical test uses the `HttpClient` and helper methods to process the request and the response:
 
-   [!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/9.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/IndexPageTests.cs?name=snippet2)]
+   :::zone pivot="xunit"
+
+   :::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs" id="snippet2":::
+
+   :::zone-end
+   :::zone pivot="mstest"
+
+   :::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs" id="snippet2":::
+
+   :::zone-end
+   :::zone pivot="nunit"
+
+   :::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs" id="snippet2":::
+
+   :::zone-end
 
 Any POST request to the SUT must satisfy the antiforgery check that's automatically made by the app's [data protection antiforgery system](xref:security/data-protection/introduction). In order to arrange for a test's POST request, the test app must:
 
@@ -139,7 +195,21 @@ The `Post_DeleteMessageHandler_ReturnsRedirectToRoot` test method of the [sample
 
 Because another test in the `IndexPageTests` class performs an operation that deletes all of the records in the database and may run before the `Post_DeleteMessageHandler_ReturnsRedirectToRoot` method, the database is reseeded in this test method to ensure that a record is present for the SUT to delete. Selecting the first delete button of the `messages` form in the SUT is simulated in the request to the SUT:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/9.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/IndexPageTests.cs?name=snippet3)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs" id="snippet3":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs" id="snippet3":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs" id="snippet3":::
+
+:::zone-end
 
 ## Client options
 
@@ -147,7 +217,21 @@ See the <xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOption
 
 Create the `WebApplicationFactoryClientOptions` class and pass it to the <xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory%601.CreateClient> method:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/9.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/IndexPageTests.cs?name=snippet1)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs" id="snippet1":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs" id="snippet1":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs" id="snippet1":::
+
+:::zone-end
 
 ***NOTE:*** To avoid HTTPS redirection warnings in logs when using HTTPS Redirection Middleware, set `BaseAddress = new Uri("https://localhost")`
 
@@ -192,11 +276,39 @@ To test the service and quote injection in an integration test, a mock service i
 
 `IntegrationTests.IndexPageTests.cs`:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/9.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/IndexPageTests.cs?name=snippet4)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs" id="snippet4":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs" id="snippet4":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs" id="snippet4":::
+
+:::zone-end
 
 `ConfigureTestServices` is called, and the scoped service is registered:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/9.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/IndexPageTests.cs?name=snippet5&highlight=7-10,17,20-21)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs" id="snippet5" highlight="7-10,17,20-21":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs" id="snippet5" highlight="7-10,17,20-21":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs" id="snippet5" highlight="7-10,17,20-22":::
+
+:::zone-end
 
 The markup produced during the test's execution reflects the quote text supplied by `TestQuoteService`, thus the assertion passes:
 
@@ -218,7 +330,21 @@ In the SUT, the `/SecurePage` page uses an <xref:Microsoft.Extensions.Dependency
 
 In the `Get_SecurePageRedirectsAnUnauthenticatedUser` test, a <xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions> is set to disallow redirects by setting <xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions.AllowAutoRedirect> to `false`:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/9.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/AuthTests.cs?name=snippet2)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/AuthTests.cs" id="snippet2":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/AuthTests.cs" id="snippet2":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/AuthTests.cs" id="snippet2":::
+
+:::zone-end
 
 By disallowing the client to follow the redirect, the following checks can be made:
 
@@ -227,11 +353,39 @@ By disallowing the client to follow the redirect, the following checks can be ma
 
 The test app can mock an <xref:Microsoft.AspNetCore.Authentication.AuthenticationHandler%601> in <xref:Microsoft.AspNetCore.TestHost.WebHostBuilderExtensions.ConfigureTestServices%2A> in order to test aspects of authentication and authorization. A minimal scenario returns an <xref:Microsoft.AspNetCore.Authentication.AuthenticateResult.Success%2A?displayProperty=nameWithType>:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/9.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/AuthTests.cs?name=snippet4&highlight=11-18)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/AuthTests.cs" id="snippet4" highlight="11-18":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/AuthTests.cs" id="snippet4" highlight="11-18":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/AuthTests.cs" id="snippet4" highlight="11-18":::
+
+:::zone-end
 
 The `TestAuthHandler` is called to authenticate a user when the authentication scheme is set to `TestScheme` where `AddAuthentication` is registered for `ConfigureTestServices`. It's important for the `TestScheme` scheme to match the scheme your app expects. Otherwise, authentication won't work.
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/9.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/AuthTests.cs?name=snippet3&highlight=7-12)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/xunit/IntegrationTests/AuthTests.cs" id="snippet3" highlight="7-12":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/mstest/IntegrationTests/AuthTests.cs" id="snippet3" highlight="7-12":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/nunit/IntegrationTests/AuthTests.cs" id="snippet3" highlight="7-12":::
+
+:::zone-end
 
 For more information on `WebApplicationFactoryClientOptions`, see the [Client options](#client-options) section.
 
@@ -243,7 +397,21 @@ See [this GitHub repository](https://github.com/blowdart/idunno.Authentication/t
 
 Set the [environment](xref:fundamentals/environments) in the custom application factory:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/9.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/CustomWebApplicationFactory.cs?name=snippet1&highlight=36)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/xunit/CustomWebApplicationFactory.cs" id="snippet1" highlight="36":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/mstest/CustomWebApplicationFactory.cs" id="snippet1" highlight="36":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="~/test/integration-tests/snippets/nunit/CustomWebApplicationFactory.cs" id="snippet1" highlight="36":::
+
+:::zone-end
 
 ## How the test infrastructure infers the app content root path
 
@@ -263,8 +431,22 @@ To disable shadow copying when using xUnit, create a `xunit.runner.json` file in
 
 ## Disposal of objects
 
+:::zone pivot="xunit"
+
 After the tests of the `IClassFixture` implementation are executed, <xref:Microsoft.AspNetCore.TestHost.TestServer> and <xref:System.Net.Http.HttpClient> are disposed when xUnit disposes of the [`WebApplicationFactory`](xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory%601). If objects instantiated by the developer require disposal, dispose of them in the `IClassFixture` implementation. For more information, see [Implementing a Dispose method](/dotnet/standard/garbage-collection/implementing-dispose).
 
+:::zone-end
+:::zone pivot="mstest"
+
+After the tests of the `TestClass` are executed, <xref:Microsoft.AspNetCore.TestHost.TestServer> and <xref:System.Net.Http.HttpClient> are disposed when MSTest disposes of the [`WebApplicationFactory`](xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory%601) in the `ClassCleanup` method. If objects instantiated by the developer require disposal, dispose of them in the `ClassCleanup` method. For more information, see [Implementing a Dispose method](/dotnet/standard/garbage-collection/implementing-dispose).
+
+:::zone-end
+:::zone pivot="nunit"
+
+After the tests of the test class are executed, <xref:Microsoft.AspNetCore.TestHost.TestServer> and <xref:System.Net.Http.HttpClient> are disposed when NUnit disposes of the [`WebApplicationFactory`](xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory%601) in the `TearDown` method. If objects instantiated by the developer require disposal, dispose of them in the `TearDown` method. For more information, see [Implementing a Dispose method](/dotnet/standard/garbage-collection/implementing-dispose).
+
+:::zone-end
+
 ## Integration tests sample
 
 The [sample app](https://github.com/dotnet/AspNetCore.Docs.Samples/tree/main/test/integration-tests/9.x/IntegrationTestsSample) is composed of two apps:
@@ -324,4 +506,4 @@ The SUT's database context is registered in `Program.cs`. The test app's `builde
 * <xref:mvc/controllers/testing>
 * [Basic tests for authentication middleware](https://github.com/blowdart/idunno.Authentication/tree/dev/test/idunno.Authentication.Basic.Test)
 
-:::moniker-end
\ No newline at end of file
+:::moniker-end
diff --git a/aspnetcore/test/integration-tests/snippets/mstest/CustomWebApplicationFactory.cs b/aspnetcore/test/integration-tests/snippets/mstest/CustomWebApplicationFactory.cs
new file mode 100644
index 000000000000..57738c369ce4
--- /dev/null
+++ b/aspnetcore/test/integration-tests/snippets/mstest/CustomWebApplicationFactory.cs
@@ -0,0 +1,51 @@
+using System.Data.Common;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.Extensions.DependencyInjection;
+using RazorPagesProject.Data;
+
+namespace RazorPagesProject.Tests;
+
+// <snippet1>
+public class CustomWebApplicationFactory<TProgram>
+    : WebApplicationFactory<TProgram> where TProgram : class
+{
+    protected override void ConfigureWebHost(IWebHostBuilder builder)
+    {
+        builder.ConfigureServices(services =>
+        {
+            var dbContextDescriptor = services.SingleOrDefault(
+                d => d.ServiceType ==
+                    typeof(IDbContextOptionsConfiguration<ApplicationDbContext>));
+
+            services.Remove(dbContextDescriptor);
+
+            var dbConnectionDescriptor = services.SingleOrDefault(
+                d => d.ServiceType ==
+                    typeof(DbConnection));
+
+            services.Remove(dbConnectionDescriptor);
+
+            // Create open SqliteConnection so EF won't automatically close it.
+            services.AddSingleton<DbConnection>(container =>
+            {
+                var connection = new SqliteConnection("DataSource=:memory:");
+                connection.Open();
+
+                return connection;
+            });
+
+            services.AddDbContext<ApplicationDbContext>((container, options) =>
+            {
+                var connection = container.GetRequiredService<DbConnection>();
+                options.UseSqlite(connection);
+            });
+        });
+
+        builder.UseEnvironment("Development");
+    }
+}
+// </snippet1>
diff --git a/aspnetcore/test/integration-tests/snippets/mstest/IntegrationTests/AuthTests.cs b/aspnetcore/test/integration-tests/snippets/mstest/IntegrationTests/AuthTests.cs
new file mode 100644
index 000000000000..54d18c487640
--- /dev/null
+++ b/aspnetcore/test/integration-tests/snippets/mstest/IntegrationTests/AuthTests.cs
@@ -0,0 +1,158 @@
+using System.Net;
+using System.Net.Http.Headers;
+using System.Security.Claims;
+using System.Text.Encodings.Web;
+using AngleSharp.Html.Dom;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using RazorPagesProject.Services;
+using RazorPagesProject.Tests.Helpers;
+
+namespace RazorPagesProject.Tests.IntegrationTests;
+
+[TestClass]
+public class AuthTests
+{
+
+    private static CustomWebApplicationFactory<Program> _factory;
+
+    [ClassInitialize]
+    public static void AssemblyInitialize(TestContext _)
+    {
+        _factory = new CustomWebApplicationFactory<Program>();
+    }
+
+    [ClassCleanup(ClassCleanupBehavior.EndOfClass)]
+    public static void AssemblyCleanup(TestContext _)
+    {
+        _factory.Dispose();
+    }
+
+    // <snippet1>
+    [TestMethod]
+    public async Task Get_GithubProfilePageCanGetAGithubUser()
+    {
+        // Arrange
+        void ConfigureTestServices(IServiceCollection services) =>
+            services.AddSingleton<IGithubClient>(new TestGithubClient());
+        var client = _factory
+            .WithWebHostBuilder(builder =>
+                builder.ConfigureTestServices(ConfigureTestServices))
+            .CreateClient();
+
+        // Act
+        var profile = await client.GetAsync("/GithubProfile");
+        Assert.AreEqual(HttpStatusCode.OK, profile.StatusCode);
+        var profileHtml = await HtmlHelpers.GetDocumentAsync(profile);
+
+        var profileWithUserName = await client.SendAsync(
+            (IHtmlFormElement)profileHtml.QuerySelector("#user-profile"),
+            new Dictionary<string, string> { ["Input_UserName"] = "user" });
+
+        // Assert
+        Assert.AreEqual(HttpStatusCode.OK, profileWithUserName.StatusCode);
+        var profileWithUserHtml =
+            await HtmlHelpers.GetDocumentAsync(profileWithUserName);
+        var userLogin = profileWithUserHtml.QuerySelector("#user-login");
+        Assert.AreEqual("user", userLogin.TextContent);
+    }
+
+    public class TestGithubClient : IGithubClient
+    {
+        public Task<GithubUser> GetUserAsync(string userName)
+        {
+            if (userName == "user")
+            {
+                return Task.FromResult(
+                    new GithubUser
+                    {
+                        Login = "user",
+                        Company = "Contoso Blockchain",
+                        Name = "John Doe"
+                    });
+            }
+            else
+            {
+                return Task.FromResult<GithubUser>(null);
+            }
+        }
+    }
+    // </snippet1>
+
+    // <snippet2>
+    [TestMethod]
+    public async Task Get_SecurePageRedirectsAnUnauthenticatedUser()
+    {
+        // Arrange
+        var client = _factory.CreateClient(
+            new WebApplicationFactoryClientOptions
+            {
+                AllowAutoRedirect = false
+            });
+
+        // Act
+        var response = await client.GetAsync("/SecurePage");
+
+        // Assert
+        Assert.AreEqual(HttpStatusCode.Redirect, response.StatusCode);
+        StringAssert.StartsWith(response.Headers.Location.OriginalString, "http://localhost/Identity/Account/Login");
+    }
+    // </snippet2>
+
+    // <snippet3>
+    [TestMethod]
+    public async Task Get_SecurePageIsReturnedForAnAuthenticatedUser()
+    {
+        // Arrange
+        var client = _factory.WithWebHostBuilder(builder =>
+        {
+            builder.ConfigureTestServices(services =>
+            {
+                services.AddAuthentication(defaultScheme: "TestScheme")
+                    .AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
+                        "TestScheme", options => { });
+            });
+        })
+            .CreateClient(new WebApplicationFactoryClientOptions
+            {
+                AllowAutoRedirect = false,
+            });
+
+        client.DefaultRequestHeaders.Authorization =
+            new AuthenticationHeaderValue(scheme: "TestScheme");
+
+        //Act
+        var response = await client.GetAsync("/SecurePage");
+
+        // Assert
+        Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
+    }
+    // </snippet3>
+}
+
+// <snippet4>
+public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
+{
+    public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
+        ILoggerFactory logger, UrlEncoder encoder)
+        : base(options, logger, encoder)
+    {
+    }
+
+    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
+    {
+        var claims = new[] { new Claim(ClaimTypes.Name, "Test user") };
+        var identity = new ClaimsIdentity(claims, "Test");
+        var principal = new ClaimsPrincipal(identity);
+        var ticket = new AuthenticationTicket(principal, "TestScheme");
+
+        var result = AuthenticateResult.Success(ticket);
+
+        return Task.FromResult(result);
+    }
+}
+// </snippet4>
diff --git a/aspnetcore/test/integration-tests/snippets/mstest/IntegrationTests/BasicTests.cs b/aspnetcore/test/integration-tests/snippets/mstest/IntegrationTests/BasicTests.cs
new file mode 100644
index 000000000000..481b6110dddb
--- /dev/null
+++ b/aspnetcore/test/integration-tests/snippets/mstest/IntegrationTests/BasicTests.cs
@@ -0,0 +1,41 @@
+namespace RazorPagesProject.Tests.IntegrationTests;
+
+// <snippet1>
+[TestClass]
+public class BasicTests
+{
+    private static CustomWebApplicationFactory<Program> _factory;
+
+    [ClassInitialize]
+    public static void AssemblyInitialize(TestContext _)
+    {
+        _factory = new CustomWebApplicationFactory<Program>();
+    }
+
+    [ClassCleanup(ClassCleanupBehavior.EndOfClass)]
+    public static void AssemblyCleanup(TestContext _)
+    {
+        _factory.Dispose();
+    }
+
+    [TestMethod]
+    [DataRow("/")]
+    [DataRow("/Index")]
+    [DataRow("/About")]
+    [DataRow("/Privacy")]
+    [DataRow("/Contact")]
+    public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
+    {
+        // Arrange
+        var client = _factory.CreateClient();
+
+        // Act
+        var response = await client.GetAsync(url);
+
+        // Assert
+        response.EnsureSuccessStatusCode(); // Status Code 200-299
+        Assert.AreEqual("text/html; charset=utf-8",
+            response.Content.Headers.ContentType.ToString());
+    }
+}
+// </snippet1>
diff --git a/aspnetcore/test/integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs b/aspnetcore/test/integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs
new file mode 100644
index 000000000000..8d9f4ab53ec0
--- /dev/null
+++ b/aspnetcore/test/integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs
@@ -0,0 +1,191 @@
+using System.Net;
+using AngleSharp.Html.Dom;
+using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.DependencyInjection;
+using RazorPagesProject.Data;
+using RazorPagesProject.Services;
+using RazorPagesProject.Tests.Helpers;
+
+namespace RazorPagesProject.Tests.IntegrationTests;
+
+// <snippet1>
+[TestClass]
+public class IndexPageTests
+{
+    private static HttpClient _client;
+    private static CustomWebApplicationFactory<Program>
+        _factory;
+
+    [ClassInitialize]
+    public static void AssemblyInitialize(TestContext _)
+    {
+        _factory = new CustomWebApplicationFactory<Program>();
+        _client = _factory.CreateClient(new WebApplicationFactoryClientOptions
+        {
+            AllowAutoRedirect = false
+        });
+    }
+
+    [ClassCleanup(ClassCleanupBehavior.EndOfClass)]
+    public static void AssemblyCleanup(TestContext _)
+    {
+        _factory.Dispose();
+    }
+    // </snippet1>
+
+    // <snippet2>
+    [TestMethod]
+    public async Task Post_DeleteAllMessagesHandler_ReturnsRedirectToRoot()
+    {
+        // Arrange
+        var defaultPage = await _client.GetAsync("/");
+        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
+
+        //Act
+        var response = await _client.SendAsync(
+            (IHtmlFormElement)content.QuerySelector("form[id='messages']"),
+            (IHtmlButtonElement)content.QuerySelector("button[id='deleteAllBtn']"));
+
+        // Assert
+        Assert.AreEqual(HttpStatusCode.OK, defaultPage.StatusCode);
+        Assert.AreEqual(HttpStatusCode.Redirect, response.StatusCode);
+        Assert.AreEqual("/", response.Headers.Location.OriginalString);
+    }
+    // </snippet2>
+
+    // <snippet3>
+    [TestMethod]
+    public async Task Post_DeleteMessageHandler_ReturnsRedirectToRoot()
+    {
+        // Arrange
+        using (var scope = _factory.Services.CreateScope())
+        {
+            var scopedServices = scope.ServiceProvider;
+            var db = scopedServices.GetRequiredService<ApplicationDbContext>();
+
+            Utilities.ReinitializeDbForTests(db);
+        }
+
+        var defaultPage = await _client.GetAsync("/");
+        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
+
+        //Act
+        var response = await _client.SendAsync(
+            (IHtmlFormElement)content.QuerySelector("form[id='messages']"),
+            (IHtmlButtonElement)content.QuerySelector("form[id='messages']")
+                .QuerySelector("div[class='panel-body']")
+                .QuerySelector("button"));
+
+        // Assert
+        Assert.AreEqual(HttpStatusCode.OK, defaultPage.StatusCode);
+        Assert.AreEqual(HttpStatusCode.Redirect, response.StatusCode);
+        Assert.AreEqual("/", response.Headers.Location.OriginalString);
+    }
+    // </snippet3>
+
+    [TestMethod]
+    public async Task Post_AddMessageHandler_ReturnsSuccess_WhenMissingMessageText()
+    {
+        // Arrange
+        var defaultPage = await _client.GetAsync("/");
+        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
+        var messageText = string.Empty;
+
+        // Act
+        var response = await _client.SendAsync(
+            (IHtmlFormElement)content.QuerySelector("form[id='addMessage']"),
+            (IHtmlButtonElement)content.QuerySelector("button[id='addMessageBtn']"),
+            new Dictionary<string, string>
+            {
+                ["Message.Text"] = messageText
+            });
+
+        // Assert
+        Assert.AreEqual(HttpStatusCode.OK, defaultPage.StatusCode);
+        // A ModelState failure returns to Page (200-OK) and doesn't redirect.
+        response.EnsureSuccessStatusCode();
+        Assert.IsNull(response.Headers.Location?.OriginalString);
+    }
+
+    [TestMethod]
+    public async Task Post_AddMessageHandler_ReturnsSuccess_WhenMessageTextTooLong()
+    {
+        // Arrange
+        var defaultPage = await _client.GetAsync("/");
+        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
+        var messageText = new string('X', 201);
+
+        // Act
+        var response = await _client.SendAsync(
+            (IHtmlFormElement)content.QuerySelector("form[id='addMessage']"),
+            (IHtmlButtonElement)content.QuerySelector("button[id='addMessageBtn']"),
+            new Dictionary<string, string>
+            {
+                ["Message.Text"] = messageText
+            });
+
+        // Assert
+        Assert.AreEqual(HttpStatusCode.OK, defaultPage.StatusCode);
+        // A ModelState failure returns to Page (200-OK) and doesn't redirect.
+        response.EnsureSuccessStatusCode();
+        Assert.IsNull(response.Headers.Location?.OriginalString);
+    }
+
+    [TestMethod]
+    public async Task Post_AnalyzeMessagesHandler_ReturnsRedirectToRoot()
+    {
+        // Arrange
+        var defaultPage = await _client.GetAsync("/");
+        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
+
+        //Act
+        var response = await _client.SendAsync(
+            (IHtmlFormElement)content.QuerySelector("form[id='analyze']"),
+            (IHtmlButtonElement)content.QuerySelector("button[id='analyzeBtn']"));
+
+        // Assert
+        Assert.AreEqual(HttpStatusCode.OK, defaultPage.StatusCode);
+        Assert.AreEqual(HttpStatusCode.Redirect, response.StatusCode);
+        Assert.AreEqual("/", response.Headers.Location.OriginalString);
+    }
+
+    // <snippet4>
+    // Quote ©1975 BBC: The Doctor (Tom Baker); Pyramids of Mars
+    // https://www.bbc.co.uk/programmes/p00pys55
+    public class TestQuoteService : IQuoteService
+    {
+        public Task<string> GenerateQuote()
+        {
+            return Task.FromResult(
+                "Something's interfering with time, Mr. Scarman, " +
+                "and time is my business.");
+        }
+    }
+    // </snippet4>
+
+    // <snippet5>
+    [TestMethod]
+    public async Task Get_QuoteService_ProvidesQuoteInPage()
+    {
+        // Arrange
+        var client = _factory.WithWebHostBuilder(builder =>
+            {
+                builder.ConfigureTestServices(services =>
+                {
+                    services.AddScoped<IQuoteService, TestQuoteService>();
+                });
+            })
+            .CreateClient();
+
+        //Act
+        var defaultPage = await client.GetAsync("/");
+        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
+        var quoteElement = content.QuerySelector("#quote");
+
+        // Assert
+        Assert.AreEqual("Something's interfering with time, Mr. Scarman, " +
+            "and time is my business.", quoteElement.Attributes["value"].Value);
+    }
+    // </snippet5>
+}
diff --git a/aspnetcore/test/integration-tests/snippets/nunit/CustomWebApplicationFactory.cs b/aspnetcore/test/integration-tests/snippets/nunit/CustomWebApplicationFactory.cs
new file mode 100644
index 000000000000..720b8a7a60b1
--- /dev/null
+++ b/aspnetcore/test/integration-tests/snippets/nunit/CustomWebApplicationFactory.cs
@@ -0,0 +1,49 @@
+using System.Data.Common;
+using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using RazorPagesProject.Data;
+
+namespace RazorPagesProject.Tests;
+
+// <snippet1>
+public class CustomWebApplicationFactory<TProgram>
+    : WebApplicationFactory<TProgram> where TProgram : class
+{
+    protected override void ConfigureWebHost(IWebHostBuilder builder)
+    {
+        builder.ConfigureServices(services =>
+        {
+            var dbContextDescriptor = services.SingleOrDefault(
+                d => d.ServiceType ==
+                    typeof(IDbContextOptionsConfiguration<ApplicationDbContext>));
+
+            services.Remove(dbContextDescriptor);
+
+            var dbConnectionDescriptor = services.SingleOrDefault(
+                d => d.ServiceType ==
+                    typeof(DbConnection));
+
+            services.Remove(dbConnectionDescriptor);
+
+            // Create open SqliteConnection so EF won't automatically close it.
+            services.AddSingleton<DbConnection>(container =>
+            {
+                var connection = new SqliteConnection("DataSource=:memory:");
+                connection.Open();
+
+                return connection;
+            });
+
+            services.AddDbContext<ApplicationDbContext>((container, options) =>
+            {
+                var connection = container.GetRequiredService<DbConnection>();
+                options.UseSqlite(connection);
+            });
+        });
+
+        builder.UseEnvironment("Development");
+    }
+}
+// </snippet1>
diff --git a/aspnetcore/test/integration-tests/snippets/nunit/IntegrationTests/AuthTests.cs b/aspnetcore/test/integration-tests/snippets/nunit/IntegrationTests/AuthTests.cs
new file mode 100644
index 000000000000..6e374e63af14
--- /dev/null
+++ b/aspnetcore/test/integration-tests/snippets/nunit/IntegrationTests/AuthTests.cs
@@ -0,0 +1,156 @@
+using System.Net;
+using System.Net.Http.Headers;
+using System.Security.Claims;
+using System.Text.Encodings.Web;
+using AngleSharp.Html.Dom;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.Options;
+using RazorPagesProject.Services;
+using RazorPagesProject.Tests.Helpers;
+
+namespace RazorPagesProject.Tests.IntegrationTests;
+
+[TestFixture]
+public class AuthTests
+{
+    private CustomWebApplicationFactory<Program>
+        _factory;
+
+    [SetUp]
+    public void SetUp()
+    {
+        _factory = new CustomWebApplicationFactory<Program>();
+    }
+
+    [TearDown]
+    public void TearDown()
+    {
+        _factory.Dispose();
+    }
+
+    // <snippet1>
+    [Test]
+    public async Task Get_GithubProfilePageCanGetAGithubUser()
+    {
+        // Arrange
+        void ConfigureTestServices(IServiceCollection services) =>
+            services.AddSingleton<IGithubClient>(new TestGithubClient());
+        var client = _factory
+            .WithWebHostBuilder(builder =>
+                builder.ConfigureTestServices(ConfigureTestServices))
+            .CreateClient();
+
+        // Act
+        var profile = await client.GetAsync("/GithubProfile");
+        Assert.That(profile.StatusCode, Is.EqualTo(HttpStatusCode.OK));
+        var profileHtml = await HtmlHelpers.GetDocumentAsync(profile);
+
+        var profileWithUserName = await client.SendAsync(
+            (IHtmlFormElement)profileHtml.QuerySelector("#user-profile"),
+            new Dictionary<string, string> { ["Input_UserName"] = "user" });
+
+        // Assert
+        Assert.That(profileWithUserName.StatusCode, Is.EqualTo(HttpStatusCode.OK));
+        var profileWithUserHtml =
+            await HtmlHelpers.GetDocumentAsync(profileWithUserName);
+        var userLogin = profileWithUserHtml.QuerySelector("#user-login");
+        Assert.That(userLogin.TextContent, Is.EqualTo("user"));
+    }
+
+    public class TestGithubClient : IGithubClient
+    {
+        public Task<GithubUser> GetUserAsync(string userName)
+        {
+            if (userName == "user")
+            {
+                return Task.FromResult(
+                    new GithubUser
+                    {
+                        Login = "user",
+                        Company = "Contoso Blockchain",
+                        Name = "John Doe"
+                    });
+            }
+            else
+            {
+                return Task.FromResult<GithubUser>(null);
+            }
+        }
+    }
+    // </snippet1>
+
+    // <snippet2>
+    [Test]
+    public async Task Get_SecurePageRedirectsAnUnauthenticatedUser()
+    {
+        // Arrange
+        var client = _factory.CreateClient(
+            new WebApplicationFactoryClientOptions
+            {
+                AllowAutoRedirect = false
+            });
+
+        // Act
+        var response = await client.GetAsync("/SecurePage");
+
+        // Assert
+        Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Redirect));
+        Assert.That(response.Headers.Location.OriginalString, Does.StartWith("http://localhost/Identity/Account/Login"));
+    }
+    // </snippet2>
+
+    // <snippet3>
+    [Test]
+    public async Task Get_SecurePageIsReturnedForAnAuthenticatedUser()
+    {
+        // Arrange
+        var client = _factory.WithWebHostBuilder(builder =>
+        {
+            builder.ConfigureTestServices(services =>
+            {
+                services.AddAuthentication(defaultScheme: "TestScheme")
+                    .AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
+                        "TestScheme", options => { });
+            });
+        })
+            .CreateClient(new WebApplicationFactoryClientOptions
+            {
+                AllowAutoRedirect = false,
+            });
+
+        client.DefaultRequestHeaders.Authorization =
+            new AuthenticationHeaderValue(scheme: "TestScheme");
+
+        //Act
+        var response = await client.GetAsync("/SecurePage");
+
+        // Assert
+        Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
+    }
+    // </snippet3>
+}
+
+// <snippet4>
+public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
+{
+    public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
+        ILoggerFactory logger, UrlEncoder encoder)
+        : base(options, logger, encoder)
+    {
+    }
+
+    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
+    {
+        var claims = new[] { new Claim(ClaimTypes.Name, "Test user") };
+        var identity = new ClaimsIdentity(claims, "Test");
+        var principal = new ClaimsPrincipal(identity);
+        var ticket = new AuthenticationTicket(principal, "TestScheme");
+
+        var result = AuthenticateResult.Success(ticket);
+
+        return Task.FromResult(result);
+    }
+}
+// </snippet4>
diff --git a/aspnetcore/test/integration-tests/snippets/nunit/IntegrationTests/BasicTests.cs b/aspnetcore/test/integration-tests/snippets/nunit/IntegrationTests/BasicTests.cs
new file mode 100644
index 000000000000..9e82ba66c951
--- /dev/null
+++ b/aspnetcore/test/integration-tests/snippets/nunit/IntegrationTests/BasicTests.cs
@@ -0,0 +1,38 @@
+namespace RazorPagesProject.Tests.IntegrationTests;
+
+// <snippet1>
+public class BasicTests
+{
+    private CustomWebApplicationFactory<Program>
+        _factory;
+
+    [SetUp]
+    public void SetUp()
+    {
+        _factory = new CustomWebApplicationFactory<Program>();
+    }
+
+    [TearDown]
+    public void TearDown()
+    {
+        _factory.Dispose();
+    }
+
+    [DatapointSource]
+    public string[] values = ["/", "/Index", "/About", "/Privacy", "/Contact"];
+
+    [Theory]
+    public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
+    {
+        // Arrange
+        var client = _factory.CreateClient();
+
+        // Act
+        var response = await client.GetAsync(url);
+
+        // Assert
+        response.EnsureSuccessStatusCode(); // Status Code 200-299
+        Assert.That(response.Content.Headers.ContentType.ToString(), Is.EqualTo("text/html; charset=utf-8"));
+    }
+}
+// </snippet1>
diff --git a/aspnetcore/test/integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs b/aspnetcore/test/integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs
new file mode 100644
index 000000000000..ff19cec8b28a
--- /dev/null
+++ b/aspnetcore/test/integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs
@@ -0,0 +1,192 @@
+using System.Net;
+using AngleSharp.Html.Dom;
+using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.AspNetCore.TestHost;
+using RazorPagesProject.Data;
+using RazorPagesProject.Services;
+using RazorPagesProject.Tests.Helpers;
+
+namespace RazorPagesProject.Tests.IntegrationTests;
+
+// <snippet1>
+public class IndexPageTests
+{
+
+    private HttpClient _client;
+    private CustomWebApplicationFactory<Program>
+        _factory;
+
+    [SetUp]
+    public void SetUp()
+    {
+        _factory = new CustomWebApplicationFactory<Program>();
+        _client = _factory.CreateClient(new WebApplicationFactoryClientOptions
+        {
+            AllowAutoRedirect = false
+        });
+    }
+
+    [TearDown]
+    public void TearDown()
+    {
+        _factory.Dispose();
+        _client.Dispose();
+    }
+    // </snippet1>
+
+    // <snippet2>
+    [Test]
+    public async Task Post_DeleteAllMessagesHandler_ReturnsRedirectToRoot()
+    {
+        // Arrange
+        var defaultPage = await _client.GetAsync("/");
+        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
+
+        //Act
+        var response = await _client.SendAsync(
+            (IHtmlFormElement)content.QuerySelector("form[id='messages']"),
+            (IHtmlButtonElement)content.QuerySelector("button[id='deleteAllBtn']"));
+
+        // Assert
+        Assert.That(defaultPage.StatusCode, Is.EqualTo(HttpStatusCode.OK));
+        Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Redirect));
+        Assert.That(response.Headers.Location.OriginalString, Is.EqualTo("/"));
+    }
+    // </snippet2>
+
+    // <snippet3>
+    [Test]
+    public async Task Post_DeleteMessageHandler_ReturnsRedirectToRoot()
+    {
+        // Arrange
+        using (var scope = _factory.Services.CreateScope())
+        {
+            var scopedServices = scope.ServiceProvider;
+            var db = scopedServices.GetRequiredService<ApplicationDbContext>();
+
+            Utilities.ReinitializeDbForTests(db);
+        }
+
+        var defaultPage = await _client.GetAsync("/");
+        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
+
+        //Act
+        var response = await _client.SendAsync(
+            (IHtmlFormElement)content.QuerySelector("form[id='messages']"),
+            (IHtmlButtonElement)content.QuerySelector("form[id='messages']")
+                .QuerySelector("div[class='panel-body']")
+                .QuerySelector("button"));
+
+        // Assert
+        Assert.That(defaultPage.StatusCode, Is.EqualTo(HttpStatusCode.OK));
+        Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Redirect));
+        Assert.That(response.Headers.Location.OriginalString, Is.EqualTo("/"));
+    }
+    // </snippet3>
+
+    [Test]
+    public async Task Post_AddMessageHandler_ReturnsSuccess_WhenMissingMessageText()
+    {
+        // Arrange
+        var defaultPage = await _client.GetAsync("/");
+        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
+        var messageText = string.Empty;
+
+        // Act
+        var response = await _client.SendAsync(
+            (IHtmlFormElement)content.QuerySelector("form[id='addMessage']"),
+            (IHtmlButtonElement)content.QuerySelector("button[id='addMessageBtn']"),
+            new Dictionary<string, string>
+            {
+                ["Message.Text"] = messageText
+            });
+
+        // Assert
+        Assert.That(defaultPage.StatusCode, Is.EqualTo(HttpStatusCode.OK));
+        // A ModelState failure returns to Page (200-OK) and doesn't redirect.
+        Assert.That(response.IsSuccessStatusCode, Is.True);
+        Assert.That(response.Headers.Location?.OriginalString, Is.Null);
+    }
+
+    [Test]
+    public async Task Post_AddMessageHandler_ReturnsSuccess_WhenMessageTextTooLong()
+    {
+        // Arrange
+        var defaultPage = await _client.GetAsync("/");
+        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
+        var messageText = new string('X', 201);
+
+        // Act
+        var response = await _client.SendAsync(
+            (IHtmlFormElement)content.QuerySelector("form[id='addMessage']"),
+            (IHtmlButtonElement)content.QuerySelector("button[id='addMessageBtn']"),
+            new Dictionary<string, string>
+            {
+                ["Message.Text"] = messageText
+            });
+
+        // Assert
+        Assert.That(defaultPage.StatusCode, Is.EqualTo(HttpStatusCode.OK));
+        // A ModelState failure returns to Page (200-OK) and doesn't redirect.
+        Assert.That(response.IsSuccessStatusCode, Is.True);
+        Assert.That(response.Headers.Location?.OriginalString, Is.Null);
+    }
+
+    [Test]
+    public async Task Post_AnalyzeMessagesHandler_ReturnsRedirectToRoot()
+    {
+        // Arrange
+        var defaultPage = await _client.GetAsync("/");
+        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
+
+        //Act
+        var response = await _client.SendAsync(
+            (IHtmlFormElement)content.QuerySelector("form[id='analyze']"),
+            (IHtmlButtonElement)content.QuerySelector("button[id='analyzeBtn']"));
+
+        // Assert
+        Assert.That(defaultPage.StatusCode, Is.EqualTo(HttpStatusCode.OK));
+        Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Redirect));
+        Assert.That(response.Headers.Location.OriginalString, Is.EqualTo("/"));
+    }
+
+    // <snippet4>
+    // Quote ©1975 BBC: The Doctor (Tom Baker); Pyramids of Mars
+    // https://www.bbc.co.uk/programmes/p00pys55
+    public class TestQuoteService : IQuoteService
+    {
+        public Task<string> GenerateQuote()
+        {
+            return Task.FromResult(
+                "Something's interfering with time, Mr. Scarman, " +
+                "and time is my business.");
+        }
+    }
+    // </snippet4>
+
+    // <snippet5>
+    [Test]
+    public async Task Get_QuoteService_ProvidesQuoteInPage()
+    {
+        // Arrange
+        var client = _factory.WithWebHostBuilder(builder =>
+            {
+                builder.ConfigureTestServices(services =>
+                {
+                    services.AddScoped<IQuoteService, TestQuoteService>();
+                });
+            })
+            .CreateClient();
+
+        //Act
+        var defaultPage = await client.GetAsync("/");
+        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
+        var quoteElement = content.QuerySelector("#quote");
+
+        // Assert
+        Assert.That(quoteElement.Attributes["value"].Value, Is.EqualTo(
+            "Something's interfering with time, Mr. Scarman, " +
+            "and time is my business."));
+    }
+    // </snippet5>
+}
diff --git a/aspnetcore/test/integration-tests/snippets/xunit/CustomWebApplicationFactory.cs b/aspnetcore/test/integration-tests/snippets/xunit/CustomWebApplicationFactory.cs
new file mode 100644
index 000000000000..0faad8f8f33d
--- /dev/null
+++ b/aspnetcore/test/integration-tests/snippets/xunit/CustomWebApplicationFactory.cs
@@ -0,0 +1,49 @@
+using System.Data.Common;
+using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using RazorPagesProject.Data;
+
+namespace RazorPagesProject.Tests;
+
+// <snippet1>
+public class CustomWebApplicationFactory<TProgram>
+    : WebApplicationFactory<TProgram> where TProgram : class
+{
+    protected override void ConfigureWebHost(IWebHostBuilder builder)
+    {
+        builder.ConfigureServices(services =>
+        {
+            var dbContextDescriptor = services.SingleOrDefault(
+                d => d.ServiceType == 
+                    typeof(IDbContextOptionsConfiguration<ApplicationDbContext>));
+
+            services.Remove(dbContextDescriptor);
+
+            var dbConnectionDescriptor = services.SingleOrDefault(
+                d => d.ServiceType ==
+                    typeof(DbConnection));
+
+            services.Remove(dbConnectionDescriptor);
+
+            // Create open SqliteConnection so EF won't automatically close it.
+            services.AddSingleton<DbConnection>(container =>
+            {
+                var connection = new SqliteConnection("DataSource=:memory:");
+                connection.Open();
+
+                return connection;
+            });
+
+            services.AddDbContext<ApplicationDbContext>((container, options) =>
+            {
+                var connection = container.GetRequiredService<DbConnection>();
+                options.UseSqlite(connection);
+            });
+        });
+
+        builder.UseEnvironment("Development");
+    }
+}
+// </snippet1>
diff --git a/aspnetcore/test/integration-tests/snippets/xunit/IntegrationTests/AuthTests.cs b/aspnetcore/test/integration-tests/snippets/xunit/IntegrationTests/AuthTests.cs
new file mode 100644
index 000000000000..0c3ae7d3fa4d
--- /dev/null
+++ b/aspnetcore/test/integration-tests/snippets/xunit/IntegrationTests/AuthTests.cs
@@ -0,0 +1,152 @@
+using System.Net;
+using System.Net.Http.Headers;
+using System.Security.Claims;
+using System.Text.Encodings.Web;
+using AngleSharp.Html.Dom;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.Options;
+using RazorPagesProject.Services;
+using RazorPagesProject.Tests.Helpers;
+using Xunit;
+
+namespace RazorPagesProject.Tests.IntegrationTests;
+
+public class AuthTests :
+    IClassFixture<CustomWebApplicationFactory<Program>>
+{
+    private readonly CustomWebApplicationFactory<Program>
+        _factory;
+
+    public AuthTests(
+        CustomWebApplicationFactory<Program> factory)
+    {
+        _factory = factory;
+    }
+
+// <snippet1>
+    [Fact]
+    public async Task Get_GithubProfilePageCanGetAGithubUser()
+    {
+        // Arrange
+        void ConfigureTestServices(IServiceCollection services) =>
+            services.AddSingleton<IGithubClient>(new TestGithubClient());
+        var client = _factory
+            .WithWebHostBuilder(builder =>
+                builder.ConfigureTestServices(ConfigureTestServices))
+            .CreateClient();
+
+        // Act
+        var profile = await client.GetAsync("/GithubProfile");
+        Assert.Equal(HttpStatusCode.OK, profile.StatusCode);
+        var profileHtml = await HtmlHelpers.GetDocumentAsync(profile);
+
+        var profileWithUserName = await client.SendAsync(
+            (IHtmlFormElement)profileHtml.QuerySelector("#user-profile"),
+            new Dictionary<string, string> { ["Input_UserName"] = "user" });
+
+        // Assert
+        Assert.Equal(HttpStatusCode.OK, profileWithUserName.StatusCode);
+        var profileWithUserHtml =
+            await HtmlHelpers.GetDocumentAsync(profileWithUserName);
+        var userLogin = profileWithUserHtml.QuerySelector("#user-login");
+        Assert.Equal("user", userLogin.TextContent);
+    }
+
+    public class TestGithubClient : IGithubClient
+    {
+        public Task<GithubUser> GetUserAsync(string userName)
+        {
+            if (userName == "user")
+            {
+                return Task.FromResult(
+                    new GithubUser
+                    {
+                        Login = "user",
+                        Company = "Contoso Blockchain",
+                        Name = "John Doe"
+                    });
+            }
+            else
+            {
+                return Task.FromResult<GithubUser>(null);
+            }
+        }
+    }
+// </snippet1>
+
+// <snippet2>
+    [Fact]
+    public async Task Get_SecurePageRedirectsAnUnauthenticatedUser()
+    {
+        // Arrange
+        var client = _factory.CreateClient(
+            new WebApplicationFactoryClientOptions
+            {
+                AllowAutoRedirect = false
+            });
+
+        // Act
+        var response = await client.GetAsync("/SecurePage");
+
+        // Assert
+        Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
+        Assert.StartsWith("http://localhost/Identity/Account/Login",
+            response.Headers.Location.OriginalString);
+    }
+// </snippet2>
+
+// <snippet3>
+    [Fact]
+    public async Task Get_SecurePageIsReturnedForAnAuthenticatedUser()
+    {
+        // Arrange
+        var client = _factory.WithWebHostBuilder(builder =>
+            {
+                builder.ConfigureTestServices(services =>
+                {
+                    services.AddAuthentication(defaultScheme: "TestScheme")
+                        .AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
+                            "TestScheme", options => { });
+                });
+            })
+            .CreateClient(new WebApplicationFactoryClientOptions
+            {
+                AllowAutoRedirect = false,
+            });
+
+        client.DefaultRequestHeaders.Authorization =
+            new AuthenticationHeaderValue(scheme: "TestScheme");
+
+        //Act
+        var response = await client.GetAsync("/SecurePage");
+
+        // Assert
+        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+    }
+// </snippet3>
+}
+
+// <snippet4>
+public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
+{
+    public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
+        ILoggerFactory logger, UrlEncoder encoder)
+        : base(options, logger, encoder)
+    {
+    }
+
+    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
+    {
+        var claims = new[] { new Claim(ClaimTypes.Name, "Test user") };
+        var identity = new ClaimsIdentity(claims, "Test");
+        var principal = new ClaimsPrincipal(identity);
+        var ticket = new AuthenticationTicket(principal, "TestScheme");
+
+        var result = AuthenticateResult.Success(ticket);
+
+        return Task.FromResult(result);
+    }
+}
+// </snippet4>
diff --git a/aspnetcore/test/integration-tests/snippets/xunit/IntegrationTests/BasicTests.cs b/aspnetcore/test/integration-tests/snippets/xunit/IntegrationTests/BasicTests.cs
new file mode 100644
index 000000000000..50594787f8d2
--- /dev/null
+++ b/aspnetcore/test/integration-tests/snippets/xunit/IntegrationTests/BasicTests.cs
@@ -0,0 +1,37 @@
+using Microsoft.AspNetCore.Mvc.Testing;
+using Xunit;
+
+namespace RazorPagesProject.Tests.IntegrationTests;
+
+// <snippet1>
+public class BasicTests 
+    : IClassFixture<WebApplicationFactory<Program>>
+{
+    private readonly WebApplicationFactory<Program> _factory;
+
+    public BasicTests(WebApplicationFactory<Program> factory)
+    {
+        _factory = factory;
+    }
+
+    [Theory]
+    [InlineData("/")]
+    [InlineData("/Index")]
+    [InlineData("/About")]
+    [InlineData("/Privacy")]
+    [InlineData("/Contact")]
+    public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
+    {
+        // Arrange
+        var client = _factory.CreateClient();
+
+        // Act
+        var response = await client.GetAsync(url);
+
+        // Assert
+        response.EnsureSuccessStatusCode(); // Status Code 200-299
+        Assert.Equal("text/html; charset=utf-8", 
+            response.Content.Headers.ContentType.ToString());
+    }
+}
+// </snippet1>
diff --git a/aspnetcore/test/integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs b/aspnetcore/test/integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs
new file mode 100644
index 000000000000..7a1f7fd1b535
--- /dev/null
+++ b/aspnetcore/test/integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs
@@ -0,0 +1,185 @@
+using System.Net;
+using AngleSharp.Html.Dom;
+using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.AspNetCore.TestHost;
+using RazorPagesProject.Data;
+using RazorPagesProject.Services;
+using RazorPagesProject.Tests.Helpers;
+using Xunit;
+
+namespace RazorPagesProject.Tests.IntegrationTests;
+
+// <snippet1>
+public class IndexPageTests :
+    IClassFixture<CustomWebApplicationFactory<Program>>
+{
+    private readonly HttpClient _client;
+    private readonly CustomWebApplicationFactory<Program>
+        _factory;
+
+    public IndexPageTests(
+        CustomWebApplicationFactory<Program> factory)
+    {
+        _factory = factory;
+        _client = factory.CreateClient(new WebApplicationFactoryClientOptions
+        {
+            AllowAutoRedirect = false
+        });
+    }
+// </snippet1>
+
+// <snippet2>
+    [Fact]
+    public async Task Post_DeleteAllMessagesHandler_ReturnsRedirectToRoot()
+    {
+        // Arrange
+        var defaultPage = await _client.GetAsync("/");
+        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
+
+        //Act
+        var response = await _client.SendAsync(
+            (IHtmlFormElement)content.QuerySelector("form[id='messages']"),
+            (IHtmlButtonElement)content.QuerySelector("button[id='deleteAllBtn']"));
+
+        // Assert
+        Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode);
+        Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
+        Assert.Equal("/", response.Headers.Location.OriginalString);
+    }
+// </snippet2>
+
+// <snippet3>
+    [Fact]
+    public async Task Post_DeleteMessageHandler_ReturnsRedirectToRoot()
+    {
+        // Arrange
+        using (var scope = _factory.Services.CreateScope())
+        {
+            var scopedServices = scope.ServiceProvider;
+            var db = scopedServices.GetRequiredService<ApplicationDbContext>();
+
+            Utilities.ReinitializeDbForTests(db);
+        }
+
+        var defaultPage = await _client.GetAsync("/");
+        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
+
+        //Act
+        var response = await _client.SendAsync(
+            (IHtmlFormElement)content.QuerySelector("form[id='messages']"),
+            (IHtmlButtonElement)content.QuerySelector("form[id='messages']")
+                .QuerySelector("div[class='panel-body']")
+                .QuerySelector("button"));
+
+        // Assert
+        Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode);
+        Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
+        Assert.Equal("/", response.Headers.Location.OriginalString);
+    }
+// </snippet3>
+
+    [Fact]
+    public async Task Post_AddMessageHandler_ReturnsSuccess_WhenMissingMessageText()
+    {
+        // Arrange
+        var defaultPage = await _client.GetAsync("/");
+        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
+        var messageText = string.Empty;
+
+        // Act
+        var response = await _client.SendAsync(
+            (IHtmlFormElement)content.QuerySelector("form[id='addMessage']"),
+            (IHtmlButtonElement)content.QuerySelector("button[id='addMessageBtn']"),
+            new Dictionary<string, string>
+            {
+                ["Message.Text"] = messageText
+            });
+
+        // Assert
+        Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode);
+        // A ModelState failure returns to Page (200-OK) and doesn't redirect.
+        response.EnsureSuccessStatusCode();
+        Assert.Null(response.Headers.Location?.OriginalString);
+    }
+
+    [Fact]
+    public async Task Post_AddMessageHandler_ReturnsSuccess_WhenMessageTextTooLong()
+    {
+        // Arrange
+        var defaultPage = await _client.GetAsync("/");
+        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
+        var messageText = new string('X', 201);
+
+        // Act
+        var response = await _client.SendAsync(
+            (IHtmlFormElement)content.QuerySelector("form[id='addMessage']"),
+            (IHtmlButtonElement)content.QuerySelector("button[id='addMessageBtn']"),
+            new Dictionary<string, string>
+            {
+                ["Message.Text"] = messageText
+            });
+
+        // Assert
+        Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode);
+        // A ModelState failure returns to Page (200-OK) and doesn't redirect.
+        response.EnsureSuccessStatusCode();
+        Assert.Null(response.Headers.Location?.OriginalString);
+    }
+
+    [Fact]
+    public async Task Post_AnalyzeMessagesHandler_ReturnsRedirectToRoot()
+    {
+        // Arrange
+        var defaultPage = await _client.GetAsync("/");
+        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
+
+        //Act
+        var response = await _client.SendAsync(
+            (IHtmlFormElement)content.QuerySelector("form[id='analyze']"),
+            (IHtmlButtonElement)content.QuerySelector("button[id='analyzeBtn']"));
+
+        // Assert
+        Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode);
+        Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
+        Assert.Equal("/", response.Headers.Location.OriginalString);
+    }
+
+// <snippet4>
+    // Quote ©1975 BBC: The Doctor (Tom Baker); Pyramids of Mars
+    // https://www.bbc.co.uk/programmes/p00pys55
+    public class TestQuoteService : IQuoteService
+    {
+        public Task<string> GenerateQuote()
+        {
+            return Task.FromResult(
+                "Something's interfering with time, Mr. Scarman, " +
+                "and time is my business.");
+        }
+    }
+// </snippet4>
+
+// <snippet5>
+    [Fact]
+    public async Task Get_QuoteService_ProvidesQuoteInPage()
+    {
+        // Arrange
+        var client = _factory.WithWebHostBuilder(builder =>
+            {
+                builder.ConfigureTestServices(services =>
+                {
+                    services.AddScoped<IQuoteService, TestQuoteService>();
+                });
+            })
+            .CreateClient();
+
+        //Act
+        var defaultPage = await client.GetAsync("/");
+        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
+        var quoteElement = content.QuerySelector("#quote");
+
+        // Assert
+        Assert.Equal("Something's interfering with time, Mr. Scarman, " +
+            "and time is my business.", quoteElement.Attributes["value"].Value);
+    }
+// </snippet5>
+}
diff --git a/aspnetcore/zone-pivot-groups.yml b/aspnetcore/zone-pivot-groups.yml
index dbe928426833..88d0a36b0be4 100644
--- a/aspnetcore/zone-pivot-groups.yml
+++ b/aspnetcore/zone-pivot-groups.yml
@@ -108,3 +108,13 @@ groups:
       title: Non-BFF pattern
     - id: bff-pattern
       title: BFF pattern
+- id: unit-testing-framework
+  title: Unit testing framework
+  prompt: Choose a unit testing framework
+  pivots:
+    - id: xunit
+      title: xUnit
+    - id: mstest
+      title: MSTest
+    - id: nunit
+      title: NUnit


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR expands the integration tests documentation to include MSTest and NUnit examples alongside the existing xUnit snippets by updating pivot groups and adding corresponding code samples.  
- Added a new pivot group ("Unit testing framework") in the zone-pivot-groups.yml file.  
- Introduced multi-framework integration test snippets for xUnit, MSTest, and NUnit across various test files (IndexPageTests, BasicTests, AuthTests, CustomWebApplicationFactory).  
- Updated integration tests markdown to reference the new unit-testing-framework pivot group.

### Reviewed Changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated no comments.

<details>
<summary>Show a summary per file</summary>

| File | Description |
| ---- | ----------- |
| aspnetcore/zone-pivot-groups.yml | Added a new pivot group for unit testing frameworks with xUnit, MSTest, and NUnit. |
| aspnetcore/test/integration-tests/snippets/xunit/* | Added xUnit integration test snippets with multiple test methods. |
| aspnetcore/test/integration-tests/snippets/mstest/* | Added MSTest integration test snippets mirroring the xUnit examples. |
| aspnetcore/test/integration-tests/snippets/nunit/* | Added NUnit integration test snippets mirroring the xUnit examples. |
| aspnetcore/test/integration-tests/includes/integration-tests9.md | Updated documentation to include new pivot zones for unit-testing-framework. |
| aspnetcore/test/integration-tests.md | Added zone_pivot_groups configuration and updated code snippet inclusions for multiple frameworks. |
</details>





state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T21:01:08Z
user: login: timdeschryver
body: The examples are looking good to me to me.
For some reason the snipping are not showing up for me while running `docfx serve`.

But I do have a couple of remarks (which can be handled in separate PRs I suppose):

- Throughout the article there are still some references to xUnit specifics, e.g. in https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-9.0#test-app-prerequisites and https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-9.0#disable-shadow-copying. What do we want to do with this?
- Should the sample be updated in the Samples repo? This means the class `HttpClientExtensions` (which is also referenced in this article) needs to be updated.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T17:36:25Z
user: login: timdeschryver
body: This is looking better, thanks for the updates @jonorogers !
Do you also want to update the code snippets in `integration-tests9.md` please.

I verified the code snippets are:

- are correct from a technical side
- are displayed (and highlighted) correctly in the docs

@Rick-Anderson when you have the time can you take a look at my previous comment and determine how you want to proceed with this please?
In summary:
- Do we want to have the samples in the Samples repository?
- Do we want to update the other references to xUnit as well in this article?


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T17:24:19Z
user: login: jonorogers
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T23:02:33Z
user: login: Rick-Anderson
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T17:17:25Z
user: login: timdeschryver
body: Looks good to me! 👍
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T08:04:49Z

Review comments:
diff_hunk: @@ -192,11 +276,39 @@ To test the service and quote injection in an integration test, a mock service i
 
 `IntegrationTests.IndexPageTests.cs`:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/9.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/IndexPageTests.cs?name=snippet4)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="../snippets/xunit/IntegrationTests/IndexPageTests.cs?name=snippet4":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="../snippets/mstest/IntegrationTests/IndexPageTests.cs?name=snippet4":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="../snippets/nunit/IntegrationTests/IndexPageTests.cs?name=snippet4":::
+
+:::zone-end
 
 `ConfigureTestServices` is called, and the scoped service is registered:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/9.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/IndexPageTests.cs?name=snippet5&highlight=7-10,17,20-21)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="../snippets/xunit/IntegrationTests/IndexPageTests.cs?name=snippet5&highlight=7-10,17,20-21":::
path: aspnetcore/test/integration-tests/includes/integration-tests9.md
user: login: timdeschryver
body: For highlights you can do the following:

```suggestion
:::code language="csharp" source="../snippets/xunit/IntegrationTests/IndexPageTests.cs" id="snippet5" highlight="7-10,17,20-21":::
```
created_at: 2025-05-02T17:35:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -66,8 +66,22 @@ Test classes implement a *class fixture* interface ([`IClassFixture`](https://xu
 The following test class, `BasicTests`, uses the `WebApplicationFactory` to bootstrap the SUT and provide an <xref:System.Net.Http.HttpClient> to a test method, `Get_EndpointsReturnSuccessAndCorrectContentType`. The method verifies the response status code is successful (200-299) and the `Content-Type` header is `text/html; charset=utf-8` for several app pages.
 
 <xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory%601.CreateClient> creates an instance of `HttpClient` that automatically follows redirects and handles cookies.
-  
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/9.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/BasicTests.cs?name=snippet1)]
+
+:::zone pivot="xunit"
+
+:::code language="csharp" source="../snippets/xunit/IntegrationTests/BasicTests.cs?name=snippet1":::
path: aspnetcore/test/integration-tests/includes/integration-tests9.md
user: login: timdeschryver
body: I think this is easier to read and recommended, I could be wrong though.

```suggestion
:::code language="csharp" source="../snippets/xunit/IntegrationTests/BasicTests.cs" id="snippet1":::
```
created_at: 2025-05-02T17:35:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -188,11 +273,39 @@ To test the service and quote injection in an integration test, a mock service i
 
 `IntegrationTests.IndexPageTests.cs`:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/10.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/IndexPageTests.cs?name=snippet4)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs" id="snippet4":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs" id="snippet4":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs" id="snippet4":::
+
+:::zone-end
 
 `ConfigureTestServices` is called, and the scoped service is registered:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/10.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/IndexPageTests.cs?name=snippet5&highlight=7-10,17,20-21)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs" id="snippet5" highlight="7-10,17,20-21":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs" id="snippet5" highlight="7-10,17,20-21":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs" id="snippet5" highlight="7-10,17,20-21":::
path: aspnetcore/test/integration-tests.md
user: login: timdeschryver
body: This needs an extra highlighted line

![image](https://github.com/user-attachments/assets/cdebb14a-5b08-4cae-a60f-c772cddb915e)


```suggestion
:::code language="csharp" source="integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs" id="snippet5" highlight="7-10,17,20-22":::
```
created_at: 2025-05-05T17:19:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -188,11 +273,39 @@ To test the service and quote injection in an integration test, a mock service i
 
 `IntegrationTests.IndexPageTests.cs`:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/10.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/IndexPageTests.cs?name=snippet4)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs" id="snippet4":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs" id="snippet4":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs" id="snippet4":::
+
+:::zone-end
 
 `ConfigureTestServices` is called, and the scoped service is registered:
 
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/10.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/IndexPageTests.cs?name=snippet5&highlight=7-10,17,20-21)]
+:::zone pivot="xunit"
+
+:::code language="csharp" source="integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs" id="snippet5" highlight="7-10,17,20-21":::
+
+:::zone-end
+:::zone pivot="mstest"
+
+:::code language="csharp" source="integration-tests/snippets/mstest/IntegrationTests/IndexPageTests.cs" id="snippet5" highlight="7-10,17,20-21":::
+
+:::zone-end
+:::zone pivot="nunit"
+
+:::code language="csharp" source="integration-tests/snippets/nunit/IntegrationTests/IndexPageTests.cs" id="snippet5" highlight="7-10,17,20-21":::
path: aspnetcore/test/integration-tests.md
user: login: jonorogers
body: Thanks, have updated the PR with the updated integration-tests9.md and both files have the highlighted line added
created_at: 2025-05-05T23:02:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -66,8 +66,22 @@ Test classes implement a *class fixture* interface ([`IClassFixture`](https://xu
 The following test class, `BasicTests`, uses the `WebApplicationFactory` to bootstrap the SUT and provide an <xref:System.Net.Http.HttpClient> to a test method, `Get_EndpointsReturnSuccessAndCorrectContentType`. The method verifies the response status code is successful (200-299) and the `Content-Type` header is `text/html; charset=utf-8` for several app pages.
 
 <xref:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory%601.CreateClient> creates an instance of `HttpClient` that automatically follows redirects and handles cookies.
-  
-[!code-csharp[](~/../AspNetCore.Docs.Samples/test/integration-tests/9.x/IntegrationTestsSample/tests/RazorPagesProject.Tests/IntegrationTests/BasicTests.cs?name=snippet1)]
+
+:::zone pivot="xunit"
+
+:::code language="csharp" source="../snippets/xunit/IntegrationTests/BasicTests.cs?name=snippet1":::
path: aspnetcore/test/integration-tests/includes/integration-tests9.md
user: login: Rick-Anderson
body: Agreed
created_at: 2025-05-08T17:17:25Z
author_association: CONTRIBUTOR

PR comments:
user: login: jonorogers
created_at: 2025-04-29T23:15:48Z
body: > @jonorogers please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.
> 
> ```
> @dotnet-policy-service agree [company="{your company}"]
> ```
> 
> > Options:
> > 
> > * (default - no company specified) I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.
> > 
> > ```
> > @dotnet-policy-service agree
> > ```
> > 
> > 
> >     
> >       
> >     
> > 
> >       
> >     
> > 
> >     
> >   
> > 
> > * (when company given) I am making Submissions in the course of work for my employer (or my employer has intellectual property rights in my Submissions by contract or applicable law). I have permission from my employer to make Submissions and enter into this Agreement on behalf of my employer. By signing below, the defined term “You” includes me and my employer.
> > 
> > ```
> > @dotnet-policy-service agree company="Microsoft"
> > ```
> 
> Contributor License Agreement

@dotnet-policy-service agree
author_association: CONTRIBUTOR
user: login: Rick-Anderson
created_at: 2025-05-01T21:04:14Z
body: @benhopkinstech @timdeschryver @profet23 please review
author_association: CONTRIBUTOR
user: login: profet23
created_at: 2025-05-02T17:56:47Z
body: Maybe tagging wrong person? Happy to take a look.
author_association: NONE
user: login: jonorogers
created_at: 2025-05-05T00:05:15Z
body: Thanks @timdeschryver , I've updated all the snippets to your provided syntax; I can see them properly loading in docfx now..

I also re-formatted the new snippets as they didn't follow the .net coding conventions (love me some K&R braces); so the highlight lines were off.

I'd be happy to update the sample instead of adding snippets if you think that would bring more value/be easier to maintain in the future.
author_association: CONTRIBUTOR
user: login: Rick-Anderson
created_at: 2025-05-08T17:29:10Z
body: Our new mandate for samples, includes, etc is to use only absolute paths, ie `test/integration-tests/xyz.cs` and never relative paths, ie, `source="integration-tests/snippets/xunit/IntegrationTests/IndexPageTests.cs"`

> Do we want to have the samples in the Samples repository?

Eventually I'd like to migrate all the samples back to this repo. We created the Samples repository so you wouldn't have to download our huge entire https://github.com/dotnet/AspNetCore.Docs repo. I don't mind mix and match, it clear from the MD file where the samples are located.
 
> Do we want to update the other references to xUnit as well in this article?

Maybe create an issue on that and do it in another PR?

@jonorogers thanks for the great work

@timdeschryver thanks for the fantastic review.
author_association: CONTRIBUTOR
user: login: jonorogers
created_at: 2025-05-09T07:02:51Z
body: No worries @Rick-Anderson ; do you want me to go back through and update the snippet references to the absolute paths?
author_association: CONTRIBUTOR
user: login: Rick-Anderson
created_at: 2025-05-11T18:18:29Z
body: > No worries @Rick-Anderson ; do you want me to go back through and update the snippet references to the absolute paths?

Yes please. A global search and replace works. All our older topics use relative paths, so when we update we convert to absolute paths.
author_association: CONTRIBUTOR
user: login: jonorogers
created_at: 2025-05-12T23:28:13Z
body: Hey @Rick-Anderson , that's done now!
author_association: CONTRIBUTOR
user: login: tdykstra
created_at: 2025-05-14T18:55:18Z
body: @jonorogers @timdeschryver  Is this good to merge now?


author_association: CONTRIBUTOR
user: login: jonorogers
created_at: 2025-05-15T08:14:17Z
body: I'm happy if you guys are, haha
author_association: CONTRIBUTOR