From 0b498f82ce24a10639a4d4f9f178b3a1cefa0296 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Sat, 22 Nov 2025 16:18:58 +0000 Subject: [PATCH 1/8] Enhance documentation for TUnit integration and usage --- docs/core/testing/index.md | 9 +- ...esting-platform-architecture-extensions.md | 3 + ...sting-platform-extensions-code-coverage.md | 3 + ...soft-testing-platform-extensions-output.md | 3 + ...esting-platform-extensions-test-reports.md | 3 + .../microsoft-testing-platform-intro.md | 2 +- docs/core/testing/mutation-testing.md | 44 +++ docs/core/testing/order-unit-tests.md | 31 +++ docs/core/testing/selective-unit-tests.md | 62 ++++- .../testing/unit-testing-best-practices.md | 18 ++ .../testing/unit-testing-code-coverage.md | 89 ++++++ .../testing/unit-testing-csharp-with-tunit.md | 256 +++++++++++++++++ .../testing/unit-testing-fsharp-with-tunit.md | 215 +++++++++++++++ .../unit-testing-visual-basic-with-tunit.md | 257 ++++++++++++++++++ .../testing/unit-testing-with-dotnet-test.md | 3 + 15 files changed, 995 insertions(+), 3 deletions(-) create mode 100644 docs/core/testing/unit-testing-csharp-with-tunit.md create mode 100644 docs/core/testing/unit-testing-fsharp-with-tunit.md create mode 100644 docs/core/testing/unit-testing-visual-basic-with-tunit.md diff --git a/docs/core/testing/index.md b/docs/core/testing/index.md index ce1cc9db5ccea..06341582d9523 100644 --- a/docs/core/testing/index.md +++ b/docs/core/testing/index.md @@ -74,7 +74,14 @@ For more information, see the following resources: #### TUnit -[TUnit](https://thomhurst.github.io/TUnit/) is entirely built on top of Microsoft.Testing.Platform and doesn't support VSTest. For more information, refer to TUnit documentation. +[TUnit](https://tunit.dev/) is a testing framework for .NET that is built entirely on top of Microsoft.Testing.Platform and doesn't support VSTest. TUnit uses source generation for test discovery and runs tests in parallel by default. + +For more information, see the following resources: + +- [TUnit official documentation](https://tunit.dev/) +- [Unit testing with C#](unit-testing-csharp-with-tunit.md) +- [Unit testing with F#](unit-testing-fsharp-with-tunit.md) +- [Unit testing with Visual Basic](unit-testing-visual-basic-with-tunit.md) #### xUnit.net diff --git a/docs/core/testing/microsoft-testing-platform-architecture-extensions.md b/docs/core/testing/microsoft-testing-platform-architecture-extensions.md index 8b32fa972276d..76d9dc69a506b 100644 --- a/docs/core/testing/microsoft-testing-platform-architecture-extensions.md +++ b/docs/core/testing/microsoft-testing-platform-architecture-extensions.md @@ -10,6 +10,9 @@ ms.date: 07/11/2024 Microsoft.Testing.Platform consists of a [testing framework](#test-framework-extension) and any number of [extensions](#other-extensibility-points) that can operate *in-process* or *out-of-process*. +> [!NOTE] +> The extensibility features described in this article work with all testing frameworks built on Microsoft.Testing.Platform, including MSTest, NUnit, xUnit, and TUnit. + As outlined in the [architecture](./microsoft-testing-platform-architecture.md) section, Microsoft.Testing.Platform is designed to accommodate a variety of scenarios and extensibility points. The primary and essential extension is undoubtedly the [testing framework](#test-framework-extension) that your tests will utilize. Failing to register this results in startup error. **The [testing framework](#test-framework-extension) is the sole mandatory extension required to execute a testing session.** To support scenarios such as generating test reports, code coverage, retrying failed tests, and other potential features, you need to provide a mechanism that allows other extensions to work in conjunction with the [testing framework](#test-framework-extension) to deliver these features not inherently provided by the [testing framework](#test-framework-extension) itself. diff --git a/docs/core/testing/microsoft-testing-platform-extensions-code-coverage.md b/docs/core/testing/microsoft-testing-platform-extensions-code-coverage.md index 11c22b6828db5..0f27e251b692f 100644 --- a/docs/core/testing/microsoft-testing-platform-extensions-code-coverage.md +++ b/docs/core/testing/microsoft-testing-platform-extensions-code-coverage.md @@ -13,6 +13,9 @@ This article lists and explains all Microsoft.Testing.Platform extensions relate You can use the code coverage feature to determine what proportion of your project's code is being tested by coded tests such as unit tests. To effectively guard against bugs, your tests should exercise or *cover* a large proportion of your code. +> [!NOTE] +> These code coverage extensions work with all testing frameworks built on Microsoft.Testing.Platform, including MSTest, NUnit, xUnit, and TUnit. For TUnit-specific code coverage examples, see [Using code coverage with TUnit](unit-testing-code-coverage.md#using-code-coverage-with-tunit). + ## Microsoft code coverage Microsoft Code Coverage analysis is possible for both managed (CLR) and unmanaged (native) code. Both static and dynamic instrumentation are supported. This extension is shipped as part of [Microsoft.Testing.Extensions.CodeCoverage](https://nuget.org/packages/Microsoft.Testing.Extensions.CodeCoverage) NuGet package. diff --git a/docs/core/testing/microsoft-testing-platform-extensions-output.md b/docs/core/testing/microsoft-testing-platform-extensions-output.md index a4ffd3554addf..55ba2611b7a98 100644 --- a/docs/core/testing/microsoft-testing-platform-extensions-output.md +++ b/docs/core/testing/microsoft-testing-platform-extensions-output.md @@ -10,6 +10,9 @@ ms.date: 08/26/2024 This article lists and explains all Microsoft.Testing.Platform extensions related to the terminal output. +> [!NOTE] +> These output extensions work with all testing frameworks built on Microsoft.Testing.Platform, including MSTest, NUnit, xUnit, and TUnit. + ## Terminal test reporter Terminal test reporter is the default implementation of status and progress reporting to the terminal (console). diff --git a/docs/core/testing/microsoft-testing-platform-extensions-test-reports.md b/docs/core/testing/microsoft-testing-platform-extensions-test-reports.md index c3de71910cd24..cdf211d5750de 100644 --- a/docs/core/testing/microsoft-testing-platform-extensions-test-reports.md +++ b/docs/core/testing/microsoft-testing-platform-extensions-test-reports.md @@ -10,6 +10,9 @@ ms.date: 04/10/2024 This article lists and explains all Microsoft.Testing.Platform extensions related to the test report capability. +> [!NOTE] +> These test report extensions work with all testing frameworks built on Microsoft.Testing.Platform, including MSTest, NUnit, xUnit, and TUnit. + A test report is a file that contains information about the execution and outcome of the tests. ## Visual Studio test reports diff --git a/docs/core/testing/microsoft-testing-platform-intro.md b/docs/core/testing/microsoft-testing-platform-intro.md index 21f5e558f600a..6a7e958a7f69d 100644 --- a/docs/core/testing/microsoft-testing-platform-intro.md +++ b/docs/core/testing/microsoft-testing-platform-intro.md @@ -41,7 +41,7 @@ The main driving factors for the evolution of the new testing platform are detai * MSTest. In MSTest, the support of `Microsoft.Testing.Platform` is done via [MSTest runner](unit-testing-mstest-runner-intro.md). * NUnit. In NUnit, the support of `Microsoft.Testing.Platform` is done via [NUnit runner](unit-testing-nunit-runner-intro.md). * xUnit.net: In xUnit.net, the support of `Microsoft.Testing.Platform` is done via [xUnit.net runner](https://xunit.net/docs/getting-started/v3/microsoft-testing-platform). -* TUnit: entirely constructed on top of the `Microsoft.Testing.Platform`, for more information, see [TUnit documentation](https://tunit.dev/). +* TUnit: Built entirely on Microsoft.Testing.Platform and doesn't support VSTest. Unlike MSTest, NUnit, and xUnit which support both VSTest and MTP, TUnit only supports MTP. For more information, see [TUnit documentation](https://tunit.dev/) and [Getting started with TUnit](unit-testing-csharp-with-tunit.md). ## Run and debug tests diff --git a/docs/core/testing/mutation-testing.md b/docs/core/testing/mutation-testing.md index 14be11aaf2fec..1bd1c05825497 100644 --- a/docs/core/testing/mutation-testing.md +++ b/docs/core/testing/mutation-testing.md @@ -169,4 +169,48 @@ public void InvalidPrice_ShouldThrowExceptionWithCorrectMessage() :::image type="content" source="media/stryker-final-report.png" lightbox="media/stryker-final-report.png" alt-text="Stryker final report"::: +## Using mutation testing with TUnit + +Mutation testing with Stryker.NET works with TUnit. The examples above can be written using TUnit's async testing syntax: + +```csharp +[Test] +public async Task ApplyDiscountCorrectly() +{ + decimal price = 100; + decimal discountPercent = 10; + + var calculator = new PriceCalculator(); + + var result = calculator.CalculatePrice(price, discountPercent); + + await Assert.That(result).IsEqualTo(90.00m); +} + +[Test] +public async Task InvalidDiscountPercent_ShouldThrowException() +{ + var calculator = new PriceCalculator(); + + await Assert.That(() => calculator.CalculatePrice(100, -1)) + .Throws(); + await Assert.That(() => calculator.CalculatePrice(100, 101)) + .Throws(); +} + +[Test] +public async Task InvalidPrice_ShouldThrowExceptionWithCorrectMessage() +{ + var calculator = new PriceCalculator(); + + var exception = await Assert.That(() => calculator.CalculatePrice(0, 10)) + .Throws(); + + await Assert.That(exception.Message) + .IsEqualTo("Price must be greater than zero."); +} +``` + +For more information about TUnit, see [Unit testing C# with TUnit](unit-testing-csharp-with-tunit.md). + Mutation testing helps to find opportunities to improve tests that make them more reliable. It forces you to check not only the 'happy path', but also complex boundary cases, reducing the likelihood of bugs in production. diff --git a/docs/core/testing/order-unit-tests.md b/docs/core/testing/order-unit-tests.md index 64b934dc803d8..1a994d4e6b3d9 100644 --- a/docs/core/testing/order-unit-tests.md +++ b/docs/core/testing/order-unit-tests.md @@ -92,6 +92,37 @@ To order tests explicitly, NUnit provides an [`OrderAttribute`](https://docs.nun :::code language="csharp" source="snippets/order-unit-tests/csharp/NUnit.TestProject/ByOrder.cs"::: +:::zone-end +:::zone pivot="tunit" + +## Order by dependency + +TUnit provides a `[DependsOn]` attribute to control test execution order through explicit dependencies. When a test depends on another, TUnit ensures the prerequisite test completes before executing the dependent test. This approach allows you to maintain test parallelism for independent tests while enforcing order where necessary. + +:::code language="csharp" source="snippets/order-unit-tests/csharp/TUnit.TestProject/ByDependency.cs"::: + +### Behavior + +* **Failure handling** - By default, if a dependency fails, the dependent test is skipped. You can override this behavior by setting `ProceedOnFailure = true` on the `DependsOnAttribute`. +* **Accessing dependent test context** - You can retrieve data from a prerequisite test's context using the `GetTests` method on the `TestContext` object. +* **Multiple dependencies** - You can apply multiple `[DependsOn]` attributes to a single test to create complex dependency chains. + +### Handling method overloads + +When depending on overloaded test methods, specify the parameter types explicitly: + +```csharp +[Test] +[DependsOn(nameof(SetupTest), [typeof(string), typeof(int)])] +public async Task DependentTest() +{ + // This test runs after SetupTest(string, int) completes +} +``` + +> [!NOTE] +> While `[DependsOn]` provides ordering capabilities, tests should ideally be self-contained, isolated, and side-effect free. Reserve dependency ordering for scenarios where independent tests are impractical, such as deployment pipelines or expensive multi-step workflows. + :::zone-end ## Next Steps diff --git a/docs/core/testing/selective-unit-tests.md b/docs/core/testing/selective-unit-tests.md index aa0d6436e3942..9577bd22a876e 100644 --- a/docs/core/testing/selective-unit-tests.md +++ b/docs/core/testing/selective-unit-tests.md @@ -31,7 +31,8 @@ dotnet test --filter | -------------- | -------------------- | | MSTest | `FullyQualifiedName`
`Name`
`ClassName`
`Priority`
`TestCategory` | | xUnit | `FullyQualifiedName`
`DisplayName`
`Traits` | - | Nunit | `FullyQualifiedName`
`Name`
`Priority`
`TestCategory` | + | NUnit | `FullyQualifiedName`
`Name`
`Priority`
`TestCategory` | + | TUnit | `FullyQualifiedName`
`Name`
`Category`
`Property` | * **Operators** @@ -230,6 +231,65 @@ dotnet test --filter "(FullyQualifiedName~UnitTest1&TestCategory=CategoryA)|Prio For more information, see [TestCase filter](https://github.com/Microsoft/vstest-docs/blob/main/docs/filter.md). +:::zone-end +:::zone pivot="tunit" + +## TUnit examples + +```csharp +using TUnit.Core; + +namespace TUnitNamespace +{ + public class UnitTest1 + { + [Test, Property("Priority", "1"), Category("CategoryA")] + public async Task TestMethod1() + { + } + + [Test, Property("Priority", "2")] + public async Task TestMethod2() + { + } + } +} +``` + +| Expression | Result | +|--|--| +| `dotnet test --filter Method` | Runs tests whose contains `Method`. | +| `dotnet test --filter Name~TestMethod1` | Runs tests whose name contains `TestMethod1`. | +| `dotnet test --filter FullyQualifiedName~TUnitNamespace.UnitTest1` | Runs tests that are in class `TUnitNamespace.UnitTest1`. | +| `dotnet test --filter FullyQualifiedName!=TUnitNamespace.UnitTest1.TestMethod1` | Runs all tests except `TUnitNamespace.UnitTest1.TestMethod1`. | +| `dotnet test --filter Category=CategoryA` | Runs tests that are annotated with `[Category("CategoryA")]`. | +| `dotnet test --filter Priority=2` | Runs tests that have `[Property("Priority", "2")]`. | + +In the code example, the `[Property]` and `[Category]` attributes can be used for filtering. + +Examples using the conditional operators `|` and `&`: + +To run tests that have `UnitTest1` in their **or** have a `Category` of `"CategoryA"`. + +```dotnetcli +dotnet test --filter "FullyQualifiedName~UnitTest1|Category=CategoryA" +``` + +To run tests that have `UnitTest1` in their **and** have a `Category` of `"CategoryA"`. + +```dotnetcli +dotnet test --filter "FullyQualifiedName~UnitTest1&Category=CategoryA" +``` + +To run tests that have either a containing `UnitTest1` **and** have a `Category` of `"CategoryA"` **or** have a `Property` with `"Priority"` of `"2"`. + +```dotnetcli +dotnet test --filter "(FullyQualifiedName~UnitTest1&Category=CategoryA)|Priority=2" +``` + +> [!NOTE] +> When running TUnit tests with Microsoft.Testing.Platform mode, the standard `dotnet test --filter` syntax is supported. Make sure you have the Microsoft.Testing.Platform mode enabled in your `global.json` file. For more information, see [Testing with dotnet test](unit-testing-with-dotnet-test.md). + :::zone-end ## See also diff --git a/docs/core/testing/unit-testing-best-practices.md b/docs/core/testing/unit-testing-best-practices.md index 353a8e74b06d1..ea05368163c48 100644 --- a/docs/core/testing/unit-testing-best-practices.md +++ b/docs/core/testing/unit-testing-best-practices.md @@ -450,6 +450,24 @@ public void GetDiscountedPrice_OnTuesday_ReturnsHalfPrice() Now the test suite has full control over the `DateTime.Now` value, and can stub any value when calling into the method. +## Async testing patterns + +When writing async tests, use async/await patterns consistently to avoid common pitfalls like `.Result` or `.Wait()` that can cause deadlocks. Most testing frameworks support async test methods. + +Example with async assertions: + +```csharp +[Test] +public async Task User_IsValid_ReturnsTrue() +{ + var user = new User { Name = "John" }; + + await Assert.That(user.IsValid()).IsTrue(); +} +``` + +Frameworks that support async tests include MSTest, NUnit, xUnit, and TUnit. Each framework provides attributes for test organization such as categories, properties, and metadata for filtering and grouping tests. These features support the "Isolated" and "Repeatable" characteristics of good unit tests described earlier in this article. + ## Related links - [Unit testing code coverage](unit-testing-code-coverage.md) diff --git a/docs/core/testing/unit-testing-code-coverage.md b/docs/core/testing/unit-testing-code-coverage.md index 824bc1281bd84..3ff6bf1dd536d 100644 --- a/docs/core/testing/unit-testing-code-coverage.md +++ b/docs/core/testing/unit-testing-code-coverage.md @@ -295,6 +295,95 @@ After running this command, an HTML file represents the generated report. :::image type="content" source="media/test-report.png" lightbox="media/test-report.png" alt-text="Unit test-generated report"::: +## Using code coverage with TUnit + +Code coverage tools like Coverlet and ReportGenerator work with TUnit projects. TUnit is built on Microsoft.Testing.Platform and supports the same code coverage workflows as other .NET testing frameworks. + +### Creating a TUnit test project with code coverage + +To create a TUnit test project with code coverage support, use the TUnit project template: + +```dotnetcli +dotnet new install TUnit.Templates +dotnet new tunit -n TUnit.Coverlet.Test +``` + +Add the project reference to your class library: + +```dotnetcli +dotnet add TUnit.Coverlet.Test\TUnit.Coverlet.Test.csproj reference Numbers\Numbers.csproj +``` + +Add the Coverlet NuGet package (if using .NET 9 SDK or earlier, use `dotnet add package` instead): + +```dotnetcli +cd TUnit.Coverlet.Test && dotnet package add coverlet.msbuild && cd .. +``` + +### TUnit test example + +TUnit tests use async/await syntax: + +```csharp +using TUnit.Assertions; +using TUnit.Assertions.Extensions; +using TUnit.Core; +using System.Numbers; + +public class PrimeServiceTests +{ + [Test] + public async Task IsPrime_InputIs1_ReturnFalse() + { + var primeService = new PrimeService(); + + bool result = primeService.IsPrime(1); + + await Assert.That(result).IsFalse(); + } + + [Test] + [Arguments(2)] + [Arguments(3)] + [Arguments(5)] + [Arguments(7)] + public async Task IsPrime_PrimesLessThan10_ReturnTrue(int value) + { + var primeService = new PrimeService(); + + bool result = primeService.IsPrime(value); + + await Assert.That(result).IsTrue(); + } +} +``` + +### Running code coverage with TUnit + +Code coverage works the same way with TUnit as with other frameworks. Since TUnit requires Microsoft.Testing.Platform mode, ensure your `global.json` includes: + +```json +{ + "test": { + "runner": "Microsoft.Testing.Platform" + } +} +``` + +Run tests with code coverage using the same commands: + +```dotnetcli +dotnet test --collect:"XPlat Code Coverage" +``` + +Or with MSBuild integration: + +```dotnetcli +dotnet test /p:CollectCoverage=true +``` + +The generated coverage reports work with ReportGenerator just like xUnit, MSTest, or NUnit projects. For more information about TUnit, see [Unit testing C# with TUnit](unit-testing-csharp-with-tunit.md). + ## See also - [Visual Studio unit test code coverage](/visualstudio/test/using-code-coverage-to-determine-how-much-code-is-being-tested) diff --git a/docs/core/testing/unit-testing-csharp-with-tunit.md b/docs/core/testing/unit-testing-csharp-with-tunit.md new file mode 100644 index 0000000000000..f7670b12d9605 --- /dev/null +++ b/docs/core/testing/unit-testing-csharp-with-tunit.md @@ -0,0 +1,256 @@ +--- +title: Unit testing C# code in .NET using dotnet test and TUnit +description: Learn unit test concepts in C# and .NET through an interactive experience building a sample solution step-by-step using dotnet test and TUnit. +author: thomhurst +ms.author: wiwagn +ms.date: 11/22/2025 +ai-usage: ai-assisted +--- +# Unit testing C# in .NET using dotnet test and TUnit + +This tutorial shows how to build a solution containing a unit test project and source code project. To follow the tutorial using a prebuilt solution, [view or download the sample code](https://github.com/dotnet/samples/tree/main/core/getting-started/unit-testing-using-dotnet-test/). For download instructions, see [Samples and Tutorials](../../samples-and-tutorials/index.md#view-and-download-samples). + +## Prerequisites + +TUnit is built entirely on [Microsoft.Testing.Platform](microsoft-testing-platform-intro.md). Unlike frameworks that support both VSTest and Microsoft.Testing.Platform, TUnit only supports Microsoft.Testing.Platform. + +## Create the solution + +In this section, a solution is created that contains the source and test projects. The completed solution has the following directory structure: + +```txt +/unit-testing-using-dotnet-test + unit-testing-using-dotnet-test.sln + /PrimeService + PrimeService.cs + PrimeService.csproj + /PrimeService.Tests + PrimeService_IsPrimeShould.cs + PrimeServiceTests.csproj +``` + +The following instructions provide the steps to create the test solution. See [Commands to create test solution](#create-test-cmd) for instructions to create the test solution in one step. + +* Open a shell window. +* Run the following command: + + ```dotnetcli + dotnet new sln -o unit-testing-using-dotnet-test + ``` + + The [`dotnet new sln`](../tools/dotnet-new.md) command creates a new solution in the *unit-testing-using-dotnet-test* directory. +* Change directory to the *unit-testing-using-dotnet-test* folder. +* Run the following command: + + ```dotnetcli + dotnet new classlib -o PrimeService + ``` + + The [`dotnet new classlib`](../tools/dotnet-new.md) command creates a new class library project in the *PrimeService* folder. The new class library will contain the code to be tested. +* Rename *Class1.cs* to *PrimeService.cs*. +* Replace the code in *PrimeService.cs* with the following code: + + ```csharp + using System; + + namespace Prime.Services + { + public class PrimeService + { + public bool IsPrime(int candidate) + { + throw new NotImplementedException("Not implemented."); + } + } + } + ``` + + Currently this code throws a , but you'll implement the method later in the tutorial. + +* In the *unit-testing-using-dotnet-test* directory, run the following command to add the class library project to the solution: + + ```dotnetcli + dotnet sln add ./PrimeService/PrimeService.csproj + ``` + +* Install the TUnit project template: + + ```dotnetcli + dotnet new install TUnit.Templates + ``` + +* Create the *PrimeService.Tests* project by running the following command: + + ```dotnetcli + dotnet new tunit -o PrimeService.Tests + ``` + + The preceding command creates the *PrimeService.Tests* project in the *PrimeService.Tests* directory. The test project uses [TUnit](https://tunit.dev/) as the test library. TUnit uses source generation for test discovery. The template configures the test runner by adding the following `` elements to the project file: + + * `TUnit` + * `TUnit.Engine` + +* Add the test project to the solution file by running the following command: + + ```dotnetcli + dotnet sln add ./PrimeService.Tests/PrimeService.Tests.csproj + ``` + +* Add the `PrimeService` class library as a dependency to the *PrimeService.Tests* project: + + ```dotnetcli + dotnet add ./PrimeService.Tests/PrimeService.Tests.csproj reference ./PrimeService/PrimeService.csproj + ``` + + + +### Commands to create the solution + +This section summarizes all the commands in the previous section. Skip this section if you've completed the steps in the previous section. + +The following commands create the test solution on a Windows machine. For macOS and Unix, update the `ren` command to the OS version of `ren` to rename a file: + +```dotnetcli +dotnet new sln -o unit-testing-using-dotnet-test +cd unit-testing-using-dotnet-test +dotnet new classlib -o PrimeService +ren .\PrimeService\Class1.cs PrimeService.cs +dotnet sln add ./PrimeService/PrimeService.csproj +dotnet new install TUnit.Templates +dotnet new tunit -o PrimeService.Tests +dotnet add ./PrimeService.Tests/PrimeService.Tests.csproj reference ./PrimeService/PrimeService.csproj +dotnet sln add ./PrimeService.Tests/PrimeService.Tests.csproj +``` + +Follow the instructions for "Replace the code in *PrimeService.cs* with the following code" in the previous section. + +## Configure Microsoft.Testing.Platform mode + +TUnit only supports Microsoft.Testing.Platform and doesn't support VSTest. To use `dotnet test` with TUnit, add the following configuration to your `global.json` file in the solution root: + +```json +{ + "test": { + "runner": "Microsoft.Testing.Platform" + } +} +``` + +This configuration enables the Microsoft.Testing.Platform mode of `dotnet test`. For more information, see [Testing with dotnet test](unit-testing-with-dotnet-test.md). + +## Create a test + +A popular approach in test driven development (TDD) is to write a (failing) test before implementing the target code. This tutorial uses the TDD approach. The `IsPrime` method is callable but not implemented. A test call to `IsPrime` fails. With TDD, you write a test that's known to fail. Then you update the target code to make the test pass. You keep repeating this approach, writing a failing test and then updating the target code to pass. + +Update the *PrimeService.Tests* project: + +* Delete *PrimeService.Tests/UnitTest1.cs*. +* Create a *PrimeService.Tests/PrimeService_IsPrimeShould.cs* file. +* Replace the code in *PrimeService_IsPrimeShould.cs* with the following code: + + ```csharp + using TUnit.Assertions; + using TUnit.Assertions.Extensions; + using Prime.Services; + + namespace Prime.UnitTests.Services; + + public class PrimeService_IsPrimeShould + { + [Test] + public async Task IsPrime_InputIs1_ReturnFalse() + { + var primeService = new PrimeService(); + bool result = primeService.IsPrime(1); + + await Assert.That(result).IsFalse(); + } + } + ``` + +The `[Test]` attribute marks a method as a test that's run by the test runner. TUnit uses source generation to discover tests at compile time. + +From the *PrimeService.Tests* folder, run `dotnet test`. The [dotnet test](../tools/dotnet-test.md) command builds both projects and runs the tests. The TUnit test runner uses Microsoft.Testing.Platform to execute the tests. + +The test fails because `IsPrime` hasn't been implemented. Using the TDD approach, write only enough code so this test passes. Update `IsPrime` with the following code: + +```csharp +public bool IsPrime(int candidate) +{ + if (candidate == 1) + { + return false; + } + throw new NotImplementedException("Not fully implemented."); +} +``` + +Run `dotnet test`. The test passes. + +### Add more tests + +Add prime number tests for 0 and -1. You *could* copy the test created in the preceding step and make copies of the following code to test 0 and -1. But don't do it, as there's a better way. + +```csharp +var primeService = new PrimeService(); +bool result = primeService.IsPrime(1); + +await Assert.That(result).IsFalse(); +``` + +Copying test code when only a parameter changes results in code duplication and test bloat. TUnit provides the `[Arguments]` attribute to specify different input values for the same test logic: + +Rather than creating new tests, apply the `[Arguments]` attribute to create parameterized tests. Replace the following code... + +```csharp +[Test] +public async Task IsPrime_InputIs1_ReturnFalse() +{ + var primeService = new PrimeService(); + bool result = primeService.IsPrime(1); + + await Assert.That(result).IsFalse(); +} +``` + +...with the following code: + +```csharp +[Test] +[Arguments(-1)] +[Arguments(0)] +[Arguments(1)] +public async Task IsPrime_ValuesLessThan2_ReturnFalse(int value) +{ + var primeService = new PrimeService(); + bool result = primeService.IsPrime(value); + + await Assert.That(result).IsFalse(); +} +``` + +In the preceding code, `[Arguments]` enables testing several values less than two. Two is the smallest prime number. Each `[Arguments]` attribute generates a separate test case, and TUnit executes these tests in parallel by default. + +Run `dotnet test`, and two of the tests fail. To make all of the tests pass, update the `IsPrime` method with the following code: + +```csharp +public bool IsPrime(int candidate) +{ + if (candidate < 2) + { + return false; + } + throw new NotImplementedException("Not fully implemented."); +} +``` + +Following the TDD approach, add more failing tests, then update the target code. See the [finished version of the tests](https://github.com/dotnet/samples/blob/main/core/getting-started/unit-testing-using-dotnet-test/PrimeService.Tests/PrimeService_IsPrimeShould.cs) and the [complete implementation of the library](https://github.com/dotnet/samples/blob/main/core/getting-started/unit-testing-using-dotnet-test/PrimeService/PrimeService.cs). + +The completed `IsPrime` method isn't an efficient algorithm for testing primality. + +### Additional resources + +* [TUnit official site](https://tunit.dev) +* [TUnit GitHub repository](https://github.com/thomhurst/TUnit) +* [Testing controller logic in ASP.NET Core](/aspnet/core/mvc/controllers/testing) +* [`dotnet reference add`](../tools/dotnet-reference-add.md) diff --git a/docs/core/testing/unit-testing-fsharp-with-tunit.md b/docs/core/testing/unit-testing-fsharp-with-tunit.md new file mode 100644 index 0000000000000..39917e533d342 --- /dev/null +++ b/docs/core/testing/unit-testing-fsharp-with-tunit.md @@ -0,0 +1,215 @@ +--- +title: Unit testing F# in .NET Core with dotnet test and TUnit +description: Learn unit test concepts for F# in .NET Core through an interactive experience building a sample solution step-by-step using dotnet test and TUnit. +author: thomhurst +ms.author: wiwagn +ms.date: 11/22/2025 +ai-usage: ai-assisted +--- +# Unit testing F# libraries in .NET Core using dotnet test and TUnit + +This tutorial takes you through an interactive experience building a sample solution step-by-step to learn unit testing concepts. If you prefer to follow the tutorial using a pre-built solution, [view or download the sample code](https://github.com/dotnet/samples/tree/main/core/getting-started/unit-testing-with-fsharp/) before you begin. For download instructions, see [Samples and Tutorials](../../samples-and-tutorials/index.md#view-and-download-samples). + +[!INCLUDE [testing an ASP.NET Core project from .NET Core](../../../includes/core-testing-note-aspnet.md)] + +## Prerequisites + +TUnit is built entirely on [Microsoft.Testing.Platform](microsoft-testing-platform-intro.md). Unlike frameworks that support both VSTest and Microsoft.Testing.Platform, TUnit only supports Microsoft.Testing.Platform. + +## Creating the source project + +Open a shell window. Create a directory called *unit-testing-with-fsharp* to hold the solution. +Inside this new directory, run `dotnet new sln` to create a new solution. This +makes it easier to manage both the class library and the unit test project. +Inside the solution directory, create a *MathService* directory. The directory and file structure thus far is shown below: + +``` +/unit-testing-with-fsharp + unit-testing-with-fsharp.sln + /MathService +``` + +Make *MathService* the current directory, and run `dotnet new classlib -lang "F#"` to create the source project. You'll create a failing implementation of the math service: + +```fsharp +module MyMath = + let squaresOfOdds xs = raise (System.NotImplementedException("You haven't written a test yet!")) +``` + +Change the directory back to the *unit-testing-with-fsharp* directory. Run `dotnet sln add .\MathService\MathService.fsproj` to add the class library project to the solution. + +## Creating the test project + +Next, create the *MathService.Tests* directory. The following outline shows the directory structure: + +``` +/unit-testing-with-fsharp + unit-testing-with-fsharp.sln + /MathService + Source Files + MathService.fsproj + /MathService.Tests +``` + +Install the TUnit project template: + +```dotnetcli +dotnet new install TUnit.Templates +``` + +Make the *MathService.Tests* directory the current directory and create a new project using `dotnet new tunit -lang "F#"`. This creates a test project that uses TUnit as the test library. TUnit uses source generation for test discovery. The generated template configures the test runner in the *MathServiceTests.fsproj*: + +```xml + + + + +``` + +The test project requires other packages to create and run unit tests. `dotnet new` in the previous step added TUnit and the TUnit engine. Now, add the `MathService` class library as another dependency to the project. Use the `dotnet reference add` command: + +```dotnetcli +dotnet reference add ../MathService/MathService.fsproj +``` + +> [!TIP] +> If you're using .NET 9 SDK or earlier, use the "verb first" form (`dotnet add reference`) instead. The "noun first" form was introduced in .NET 10. + +You can see the entire file in the [samples repository](https://github.com/dotnet/samples/blob/main/core/getting-started/unit-testing-with-fsharp/MathService.Tests/MathService.Tests.fsproj) on GitHub. + +You have the following final solution layout: + +``` +/unit-testing-with-fsharp + unit-testing-with-fsharp.sln + /MathService + Source Files + MathService.fsproj + /MathService.Tests + Test Source Files + MathServiceTests.fsproj +``` + +Execute `dotnet sln add .\MathService.Tests\MathService.Tests.fsproj` in the *unit-testing-with-fsharp* directory. + +## Configure Microsoft.Testing.Platform mode + +TUnit only supports Microsoft.Testing.Platform and doesn't support VSTest. To use `dotnet test` with TUnit, add the following configuration to your `global.json` file in the solution root: + +```json +{ + "test": { + "runner": "Microsoft.Testing.Platform" + } +} +``` + +This configuration enables the Microsoft.Testing.Platform mode of `dotnet test`. For more information, see [Testing with dotnet test](unit-testing-with-dotnet-test.md). + +## Creating the first test + +You write one failing test, make it pass, then repeat the process. Open *Tests.fs* and add the following code: + +```fsharp +open TUnit.Assertions +open TUnit.Assertions.Extensions +open TUnit.Core + +[] +let ``My test`` () = + task { + do! Assert.That(true).IsTrue() + } + +[] +let ``Fail every time`` () = + task { + do! Assert.That(false).IsTrue() + } +``` + +The `[]` attribute marks a method as a test that's run by the test runner. TUnit uses source generation to discover tests at compile time. + +From the *unit-testing-with-fsharp* directory, execute `dotnet test` to build the tests and the class library and then run the tests. The TUnit test runner uses Microsoft.Testing.Platform to execute the tests. + +These two tests show the most basic passing and failing tests. `My test` passes, and `Fail every time` fails. Now, create a test for the `squaresOfOdds` method. The `squaresOfOdds` method returns a sequence of the squares of all odd integer values that are part of the input sequence. Rather than trying to write all of those functions at once, you can iteratively create tests that validate the functionality. Making each test pass means creating the necessary functionality for the method. + +The simplest test we can write is to call `squaresOfOdds` with all even numbers, where the result should be an empty sequence of integers. Here's that test: + +```fsharp +[] +let ``Sequence of Evens returns empty collection`` () = + task { + let expected = Seq.empty + let actual = MyMath.squaresOfOdds [2; 4; 6; 8; 10] + do! Assert.That(actual).IsEquivalentTo(expected) + } +``` + +Your test fails. You haven't created the implementation yet. Make this test pass by writing the simplest code in the `MathService` class that works: + +```fsharp +let squaresOfOdds xs = + Seq.empty +``` + +In the *unit-testing-with-fsharp* directory, run `dotnet test` again. The `dotnet test` command runs a build for the `MathService` project and then for the `MathService.Tests` project. After building both projects, it runs this single test. It passes. + +## Completing the requirements + +Now that you've made one test pass, it's time to write more. The next simple case works with a sequence whose only odd number is `1`. The number 1 is easier because the square of 1 is 1. Here's that next test: + +```fsharp +[] +let ``Sequences of Ones and Evens returns Ones`` () = + task { + let expected = [1; 1; 1; 1] + let actual = MyMath.squaresOfOdds [2; 1; 4; 1; 6; 1; 8; 1; 10] + do! Assert.That(actual).IsEquivalentTo(expected) + } +``` + +Executing `dotnet test` runs your tests and shows you that the new test fails. Now, update the `squaresOfOdds` method to handle this new test. You filter all the even numbers out of the sequence to make this test pass. You can do that by writing a small filter function and using `Seq.filter`: + +```fsharp +let private isOdd x = x % 2 <> 0 + +let squaresOfOdds xs = + xs + |> Seq.filter isOdd +``` + +There's one more step to go: square each of the odd numbers. Start by writing a new test: + +```fsharp +[] +let ``SquaresOfOdds works`` () = + task { + let expected = [1; 9; 25; 49; 81] + let actual = MyMath.squaresOfOdds [1; 2; 3; 4; 5; 6; 7; 8; 9; 10] + do! Assert.That(actual).IsEquivalentTo(expected) + } +``` + +You can fix the test by piping the filtered sequence through a map operation to compute the square of each odd number: + +```fsharp +let private square x = x * x +let private isOdd x = x % 2 <> 0 + +let squaresOfOdds xs = + xs + |> Seq.filter isOdd + |> Seq.map square +``` + +You've built a small library and a set of unit tests for that library. You've structured the solution so that adding new packages and tests is part of the normal workflow. You've concentrated most of your time and effort on solving the goals of the application. + +## See also + +- [TUnit official site](https://tunit.dev) +- [TUnit GitHub repository](https://github.com/thomhurst/TUnit) +- [dotnet new](../tools/dotnet-new.md) +- [dotnet sln](../tools/dotnet-sln.md) +- [dotnet reference add](../tools/dotnet-reference-add.md) +- [dotnet test](../tools/dotnet-test.md) diff --git a/docs/core/testing/unit-testing-visual-basic-with-tunit.md b/docs/core/testing/unit-testing-visual-basic-with-tunit.md new file mode 100644 index 0000000000000..a32389610dc42 --- /dev/null +++ b/docs/core/testing/unit-testing-visual-basic-with-tunit.md @@ -0,0 +1,257 @@ +--- +title: Unit testing Visual Basic in .NET Core with dotnet test and TUnit +description: Learn unit test concepts in .NET Core through an interactive experience building a sample Visual Basic solution step-by-step using dotnet test and TUnit. +author: thomhurst +ms.author: wiwagn +ms.date: 11/22/2025 +ai-usage: ai-assisted +--- +# Unit testing Visual Basic .NET Core libraries using dotnet test and TUnit + +This tutorial shows how to build a solution containing a unit test project and library project. To follow the tutorial using a pre-built solution, [view or download the sample code](https://github.com/dotnet/samples/tree/main/core/getting-started/unit-testing-using-dotnet-test/). For download instructions, see [Samples and Tutorials](../../samples-and-tutorials/index.md#view-and-download-samples). + +## Prerequisites + +TUnit is built entirely on [Microsoft.Testing.Platform](microsoft-testing-platform-intro.md). Unlike frameworks that support both VSTest and Microsoft.Testing.Platform, TUnit only supports Microsoft.Testing.Platform. + +## Create the solution + +In this section, a solution is created that contains the source and test projects. The completed solution has the following directory structure: + +``` +/unit-testing-using-dotnet-test + unit-testing-using-dotnet-test.sln + /PrimeService + PrimeService.vb + PrimeService.vbproj + /PrimeService.Tests + PrimeService_IsPrimeShould.vb + PrimeServiceTests.vbproj +``` + +The following instructions provide the steps to create the test solution. See [Commands to create test solution](#create-test-cmd) for instructions to create the test solution in one step. + +* Open a shell window. +* Run the following command: + + ```dotnetcli + dotnet new sln -o unit-testing-using-dotnet-test + ``` + + The [`dotnet new sln`](../tools/dotnet-new.md) command creates a new solution in the *unit-testing-using-dotnet-test* directory. +* Change directory to the *unit-testing-using-dotnet-test* folder. +* Run the following command: + + ```dotnetcli + dotnet new classlib -o PrimeService -lang VB + ``` + + The [`dotnet new classlib`](../tools/dotnet-new.md) command creates a new class library project in the *PrimeService* folder. The new class library will contain the code to be tested. +* Rename *Class1.vb* to *PrimeService.vb*. +* Replace the code in *PrimeService.vb* with the following code: + + ```vb + Imports System + + Namespace Prime.Services + Public Class PrimeService + Public Function IsPrime(candidate As Integer) As Boolean + Throw New NotImplementedException("Not implemented.") + End Function + End Class + End Namespace + ``` + +* The preceding code: + * Throws a with a message indicating it's not implemented. + * Is updated later in the tutorial. + + + +* In the *unit-testing-using-dotnet-test* directory, run the following command to add the class library project to the solution: + + ```dotnetcli + dotnet sln add ./PrimeService/PrimeService.vbproj + ``` + +* Install the TUnit project template: + + ```dotnetcli + dotnet new install TUnit.Templates + ``` + +* Create the *PrimeService.Tests* project by running the following command: + + ```dotnetcli + dotnet new tunit -o PrimeService.Tests -lang VB + ``` + +* The preceding command: + * Creates the *PrimeService.Tests* project in the *PrimeService.Tests* directory. The test project uses [TUnit](https://tunit.dev/) as the test library. + * TUnit uses source generation for test discovery. + * Configures the test runner by adding the following `` elements to the project file: + * `TUnit` + * `TUnit.Engine` + +* Add the test project to the solution file by running the following command: + + ```dotnetcli + dotnet sln add ./PrimeService.Tests/PrimeService.Tests.vbproj + ``` + +* Add the `PrimeService` class library as a dependency to the *PrimeService.Tests* project: + + ```dotnetcli + dotnet add ./PrimeService.Tests/PrimeService.Tests.vbproj reference ./PrimeService/PrimeService.vbproj + ``` + + + +### Commands to create the solution + +This section summarizes all the commands in the previous section. Skip this section if you've completed the steps in the previous section. + +The following commands create the test solution on a Windows machine. For macOS and Unix, update the `ren` command to the OS version of `ren` to rename a file: + +```dotnetcli +dotnet new sln -o unit-testing-using-dotnet-test +cd unit-testing-using-dotnet-test +dotnet new classlib -o PrimeService -lang VB +ren .\PrimeService\Class1.vb PrimeService.vb +dotnet sln add ./PrimeService/PrimeService.vbproj +dotnet new install TUnit.Templates +dotnet new tunit -o PrimeService.Tests -lang VB +dotnet add ./PrimeService.Tests/PrimeService.Tests.vbproj reference ./PrimeService/PrimeService.vbproj +dotnet sln add ./PrimeService.Tests/PrimeService.Tests.vbproj +``` + +Follow the instructions for "Replace the code in *PrimeService.vb* with the following code" in the previous section. + +## Configure Microsoft.Testing.Platform mode + +TUnit only supports Microsoft.Testing.Platform and doesn't support VSTest. To use `dotnet test` with TUnit, add the following configuration to your `global.json` file in the solution root: + +```json +{ + "test": { + "runner": "Microsoft.Testing.Platform" + } +} +``` + +This configuration enables the Microsoft.Testing.Platform mode of `dotnet test`. For more information, see [Testing with dotnet test](unit-testing-with-dotnet-test.md). + +## Create a test + +A popular approach in test driven development (TDD) is to write a test before implementing the target code. This tutorial uses the TDD approach. The `IsPrime` method is callable, but not implemented. A test call to `IsPrime` fails. With TDD, a test is written that is known to fail. The target code is updated to make the test pass. You keep repeating this approach, writing a failing test and then updating the target code to pass. + +Update the *PrimeService.Tests* project: + +* Delete *PrimeService.Tests/UnitTest1.vb*. +* Create a *PrimeService.Tests/PrimeService_IsPrimeShould.vb* file. +* Replace the code in *PrimeService_IsPrimeShould.vb* with the following code: + +```vb +Imports TUnit.Assertions +Imports TUnit.Assertions.Extensions +Imports TUnit.Core + +Namespace PrimeService.Tests + Public Class PrimeService_IsPrimeShould + Private ReadOnly _primeService As Prime.Services.PrimeService + + Public Sub New() + _primeService = New Prime.Services.PrimeService() + End Sub + + + Function IsPrime_InputIs1_ReturnFalse() As Task + Return Task.Run(Async Function() + Dim result As Boolean = _primeService.IsPrime(1) + Await Assert.That(result).IsFalse() + End Function) + End Function + + End Class +End Namespace +``` + +The `[Test]` attribute marks a method as a test that's run by the test runner. TUnit uses source generation to discover tests at compile time. + +From the *PrimeService.Tests* folder, run `dotnet test`. The [dotnet test](../tools/dotnet-test.md) command builds both projects and runs the tests. The TUnit test runner uses Microsoft.Testing.Platform to execute the tests. + +The test fails because `IsPrime` hasn't been implemented. Using the TDD approach, write only enough code so this test passes. Update `IsPrime` with the following code: + +```vb +Public Function IsPrime(candidate As Integer) As Boolean + If candidate = 1 Then + Return False + End If + Throw New NotImplementedException("Not implemented.") +End Function +``` + +Run `dotnet test`. The test passes. + +### Add more tests + +Add prime number tests for 0 and -1. You could copy the preceding test and change the following code to use 0 and -1: + +```vb +Dim result As Boolean = _primeService.IsPrime(1) + +Await Assert.That(result).IsFalse() +``` + +Copying test code when only a parameter changes results in code duplication and test bloat. TUnit provides the `[Arguments]` attribute to specify different input values for the same test logic: + +Rather than creating new tests, apply the `[Arguments]` attribute to create parameterized tests. Replace the following code: + +```vb + +Function IsPrime_InputIs1_ReturnFalse() As Task + Return Task.Run(Async Function() + Dim result As Boolean = _primeService.IsPrime(1) + Await Assert.That(result).IsFalse() + End Function) +End Function +``` + +with the following code: + +```vb + + + + +Function IsPrime_ValuesLessThan2_ReturnFalse(ByVal value As Integer) As Task + Return Task.Run(Async Function() + Dim result As Boolean = _primeService.IsPrime(value) + Await Assert.That(result).IsFalse() + End Function) +End Function +``` + +In the preceding code, `[Arguments]` enables testing several values less than two. Two is the smallest prime number. Each `[Arguments]` attribute generates a separate test case, and TUnit executes these tests in parallel by default. + +Run `dotnet test`, two of the tests fail. To make all of the tests pass, update the `IsPrime` method with the following code: + +```vb +Public Function IsPrime(candidate As Integer) As Boolean + If candidate < 2 Then + Return False + End If + Throw New NotImplementedException("Not fully implemented.") +End Function +``` + +Following the TDD approach, add more failing tests, then update the target code. See the [finished version of the tests](https://github.com/dotnet/samples/blob/main/core/getting-started/unit-testing-vb-dotnet-test/PrimeService.Tests/PrimeService_IsPrimeShould.vb) and the [complete implementation of the library](https://github.com/dotnet/samples/blob/main/core/getting-started/unit-testing-vb-dotnet-test/PrimeService/PrimeService.vb). + +The completed `IsPrime` method is not an efficient algorithm for testing primality. + +### Additional resources + +- [TUnit official site](https://tunit.dev) +- [TUnit GitHub repository](https://github.com/thomhurst/TUnit) +- [Testing controller logic in ASP.NET Core](/aspnet/core/mvc/controllers/testing) +- [`dotnet reference add`](../tools/dotnet-reference-add.md) diff --git a/docs/core/testing/unit-testing-with-dotnet-test.md b/docs/core/testing/unit-testing-with-dotnet-test.md index c57dc5e2871da..6dd478d67894b 100644 --- a/docs/core/testing/unit-testing-with-dotnet-test.md +++ b/docs/core/testing/unit-testing-with-dotnet-test.md @@ -91,6 +91,9 @@ Due to these issues, .NET has introduced a new `dotnet test` mode specifically d To address the issues encountered when running `dotnet test` with MTP in VSTest mode, .NET introduced a new mode in the .NET 10 SDK that's specifically designed for MTP. +> [!NOTE] +> TUnit is built exclusively on Microsoft.Testing.Platform and doesn't support VSTest mode. When using TUnit, you must use the MTP mode of `dotnet test` as described in this section. For more information about TUnit, see [Unit testing C# with TUnit](unit-testing-csharp-with-tunit.md). + To enable this mode, add the following configuration to your `global.json` file: ```json From 890c2d7040b554183c1bba9afb50c601be8da720 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Sat, 22 Nov 2025 16:23:35 +0000 Subject: [PATCH 2/8] Remove TUnit-specific notes from various documentation files --- .../microsoft-testing-platform-architecture-extensions.md | 3 --- .../microsoft-testing-platform-extensions-code-coverage.md | 3 --- .../testing/microsoft-testing-platform-extensions-output.md | 3 --- .../microsoft-testing-platform-extensions-test-reports.md | 3 --- docs/core/testing/selective-unit-tests.md | 3 --- docs/core/testing/unit-testing-with-dotnet-test.md | 3 --- 6 files changed, 18 deletions(-) diff --git a/docs/core/testing/microsoft-testing-platform-architecture-extensions.md b/docs/core/testing/microsoft-testing-platform-architecture-extensions.md index 76d9dc69a506b..8b32fa972276d 100644 --- a/docs/core/testing/microsoft-testing-platform-architecture-extensions.md +++ b/docs/core/testing/microsoft-testing-platform-architecture-extensions.md @@ -10,9 +10,6 @@ ms.date: 07/11/2024 Microsoft.Testing.Platform consists of a [testing framework](#test-framework-extension) and any number of [extensions](#other-extensibility-points) that can operate *in-process* or *out-of-process*. -> [!NOTE] -> The extensibility features described in this article work with all testing frameworks built on Microsoft.Testing.Platform, including MSTest, NUnit, xUnit, and TUnit. - As outlined in the [architecture](./microsoft-testing-platform-architecture.md) section, Microsoft.Testing.Platform is designed to accommodate a variety of scenarios and extensibility points. The primary and essential extension is undoubtedly the [testing framework](#test-framework-extension) that your tests will utilize. Failing to register this results in startup error. **The [testing framework](#test-framework-extension) is the sole mandatory extension required to execute a testing session.** To support scenarios such as generating test reports, code coverage, retrying failed tests, and other potential features, you need to provide a mechanism that allows other extensions to work in conjunction with the [testing framework](#test-framework-extension) to deliver these features not inherently provided by the [testing framework](#test-framework-extension) itself. diff --git a/docs/core/testing/microsoft-testing-platform-extensions-code-coverage.md b/docs/core/testing/microsoft-testing-platform-extensions-code-coverage.md index 0f27e251b692f..11c22b6828db5 100644 --- a/docs/core/testing/microsoft-testing-platform-extensions-code-coverage.md +++ b/docs/core/testing/microsoft-testing-platform-extensions-code-coverage.md @@ -13,9 +13,6 @@ This article lists and explains all Microsoft.Testing.Platform extensions relate You can use the code coverage feature to determine what proportion of your project's code is being tested by coded tests such as unit tests. To effectively guard against bugs, your tests should exercise or *cover* a large proportion of your code. -> [!NOTE] -> These code coverage extensions work with all testing frameworks built on Microsoft.Testing.Platform, including MSTest, NUnit, xUnit, and TUnit. For TUnit-specific code coverage examples, see [Using code coverage with TUnit](unit-testing-code-coverage.md#using-code-coverage-with-tunit). - ## Microsoft code coverage Microsoft Code Coverage analysis is possible for both managed (CLR) and unmanaged (native) code. Both static and dynamic instrumentation are supported. This extension is shipped as part of [Microsoft.Testing.Extensions.CodeCoverage](https://nuget.org/packages/Microsoft.Testing.Extensions.CodeCoverage) NuGet package. diff --git a/docs/core/testing/microsoft-testing-platform-extensions-output.md b/docs/core/testing/microsoft-testing-platform-extensions-output.md index 55ba2611b7a98..a4ffd3554addf 100644 --- a/docs/core/testing/microsoft-testing-platform-extensions-output.md +++ b/docs/core/testing/microsoft-testing-platform-extensions-output.md @@ -10,9 +10,6 @@ ms.date: 08/26/2024 This article lists and explains all Microsoft.Testing.Platform extensions related to the terminal output. -> [!NOTE] -> These output extensions work with all testing frameworks built on Microsoft.Testing.Platform, including MSTest, NUnit, xUnit, and TUnit. - ## Terminal test reporter Terminal test reporter is the default implementation of status and progress reporting to the terminal (console). diff --git a/docs/core/testing/microsoft-testing-platform-extensions-test-reports.md b/docs/core/testing/microsoft-testing-platform-extensions-test-reports.md index cdf211d5750de..c3de71910cd24 100644 --- a/docs/core/testing/microsoft-testing-platform-extensions-test-reports.md +++ b/docs/core/testing/microsoft-testing-platform-extensions-test-reports.md @@ -10,9 +10,6 @@ ms.date: 04/10/2024 This article lists and explains all Microsoft.Testing.Platform extensions related to the test report capability. -> [!NOTE] -> These test report extensions work with all testing frameworks built on Microsoft.Testing.Platform, including MSTest, NUnit, xUnit, and TUnit. - A test report is a file that contains information about the execution and outcome of the tests. ## Visual Studio test reports diff --git a/docs/core/testing/selective-unit-tests.md b/docs/core/testing/selective-unit-tests.md index 9577bd22a876e..2dc564b204452 100644 --- a/docs/core/testing/selective-unit-tests.md +++ b/docs/core/testing/selective-unit-tests.md @@ -287,9 +287,6 @@ To run tests that have either a [!NOTE] -> When running TUnit tests with Microsoft.Testing.Platform mode, the standard `dotnet test --filter` syntax is supported. Make sure you have the Microsoft.Testing.Platform mode enabled in your `global.json` file. For more information, see [Testing with dotnet test](unit-testing-with-dotnet-test.md). - :::zone-end ## See also diff --git a/docs/core/testing/unit-testing-with-dotnet-test.md b/docs/core/testing/unit-testing-with-dotnet-test.md index 6dd478d67894b..c57dc5e2871da 100644 --- a/docs/core/testing/unit-testing-with-dotnet-test.md +++ b/docs/core/testing/unit-testing-with-dotnet-test.md @@ -91,9 +91,6 @@ Due to these issues, .NET has introduced a new `dotnet test` mode specifically d To address the issues encountered when running `dotnet test` with MTP in VSTest mode, .NET introduced a new mode in the .NET 10 SDK that's specifically designed for MTP. -> [!NOTE] -> TUnit is built exclusively on Microsoft.Testing.Platform and doesn't support VSTest mode. When using TUnit, you must use the MTP mode of `dotnet test` as described in this section. For more information about TUnit, see [Unit testing C# with TUnit](unit-testing-csharp-with-tunit.md). - To enable this mode, add the following configuration to your `global.json` file: ```json From 8b3ae55fc9a4cc974ab0145ab2976430b314936c Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Sat, 22 Nov 2025 16:33:06 +0000 Subject: [PATCH 3/8] Refactor mutation testing examples for clarity and update selective unit tests documentation with TUnit filter syntax --- docs/core/testing/mutation-testing.md | 8 +++--- docs/core/testing/selective-unit-tests.md | 25 ++++++++++--------- .../testing/unit-testing-best-practices.md | 18 ------------- 3 files changed, 16 insertions(+), 35 deletions(-) diff --git a/docs/core/testing/mutation-testing.md b/docs/core/testing/mutation-testing.md index 1bd1c05825497..afb16e276d7ad 100644 --- a/docs/core/testing/mutation-testing.md +++ b/docs/core/testing/mutation-testing.md @@ -203,11 +203,9 @@ public async Task InvalidPrice_ShouldThrowExceptionWithCorrectMessage() { var calculator = new PriceCalculator(); - var exception = await Assert.That(() => calculator.CalculatePrice(0, 10)) - .Throws(); - - await Assert.That(exception.Message) - .IsEqualTo("Price must be greater than zero."); + await Assert.That(() => calculator.CalculatePrice(0, 10)) + .Throws() + .WithMessage("Price must be greater than zero."); } ``` diff --git a/docs/core/testing/selective-unit-tests.md b/docs/core/testing/selective-unit-tests.md index 2dc564b204452..2971207b7acbc 100644 --- a/docs/core/testing/selective-unit-tests.md +++ b/docs/core/testing/selective-unit-tests.md @@ -256,35 +256,36 @@ namespace TUnitNamespace } ``` +TUnit uses the `--treenode-filter` flag with a path-based syntax: + | Expression | Result | |--|--| -| `dotnet test --filter Method` | Runs tests whose contains `Method`. | -| `dotnet test --filter Name~TestMethod1` | Runs tests whose name contains `TestMethod1`. | -| `dotnet test --filter FullyQualifiedName~TUnitNamespace.UnitTest1` | Runs tests that are in class `TUnitNamespace.UnitTest1`. | -| `dotnet test --filter FullyQualifiedName!=TUnitNamespace.UnitTest1.TestMethod1` | Runs all tests except `TUnitNamespace.UnitTest1.TestMethod1`. | -| `dotnet test --filter Category=CategoryA` | Runs tests that are annotated with `[Category("CategoryA")]`. | -| `dotnet test --filter Priority=2` | Runs tests that have `[Property("Priority", "2")]`. | +| `dotnet test --treenode-filter "/*/*/*/*Method*"` | Runs tests whose method name contains `Method`. | +| `dotnet test --treenode-filter "/*/*/*/TestMethod1"` | Runs tests whose name is `TestMethod1`. | +| `dotnet test --treenode-filter "/*/TUnitNamespace/UnitTest1/*"` | Runs all tests in class `TUnitNamespace.UnitTest1`. | +| `dotnet test --treenode-filter "/**[Category=CategoryA]"` | Runs tests that are annotated with `[Category("CategoryA")]`. | +| `dotnet test --treenode-filter "/**[Priority=2]"` | Runs tests that have `[Property("Priority", "2")]`. | In the code example, the `[Property]` and `[Category]` attributes can be used for filtering. Examples using the conditional operators `|` and `&`: -To run tests that have `UnitTest1` in their **or** have a `Category` of `"CategoryA"`. +To run tests that have `UnitTest1` in their class name **or** have a `Category` of `"CategoryA"`. ```dotnetcli -dotnet test --filter "FullyQualifiedName~UnitTest1|Category=CategoryA" +dotnet test --treenode-filter "(/*/*/UnitTest1/*)|/**[Category=CategoryA]" ``` -To run tests that have `UnitTest1` in their **and** have a `Category` of `"CategoryA"`. +To run tests that are in class `UnitTest1` **and** have a `Category` of `"CategoryA"`. ```dotnetcli -dotnet test --filter "FullyQualifiedName~UnitTest1&Category=CategoryA" +dotnet test --treenode-filter "(/*/*/UnitTest1/*)&/**[Category=CategoryA]" ``` -To run tests that have either a containing `UnitTest1` **and** have a `Category` of `"CategoryA"` **or** have a `Property` with `"Priority"` of `"2"`. +To run tests that have either class `UnitTest1` **and** `Category` of `"CategoryA"` **or** have a `Property` with `"Priority"` of `"2"`. ```dotnetcli -dotnet test --filter "(FullyQualifiedName~UnitTest1&Category=CategoryA)|Priority=2" +dotnet test --treenode-filter "((/*/*/UnitTest1/*)&/**[Category=CategoryA])|/**[Priority=2]" ``` :::zone-end diff --git a/docs/core/testing/unit-testing-best-practices.md b/docs/core/testing/unit-testing-best-practices.md index ea05368163c48..353a8e74b06d1 100644 --- a/docs/core/testing/unit-testing-best-practices.md +++ b/docs/core/testing/unit-testing-best-practices.md @@ -450,24 +450,6 @@ public void GetDiscountedPrice_OnTuesday_ReturnsHalfPrice() Now the test suite has full control over the `DateTime.Now` value, and can stub any value when calling into the method. -## Async testing patterns - -When writing async tests, use async/await patterns consistently to avoid common pitfalls like `.Result` or `.Wait()` that can cause deadlocks. Most testing frameworks support async test methods. - -Example with async assertions: - -```csharp -[Test] -public async Task User_IsValid_ReturnsTrue() -{ - var user = new User { Name = "John" }; - - await Assert.That(user.IsValid()).IsTrue(); -} -``` - -Frameworks that support async tests include MSTest, NUnit, xUnit, and TUnit. Each framework provides attributes for test organization such as categories, properties, and metadata for filtering and grouping tests. These features support the "Isolated" and "Repeatable" characteristics of good unit tests described earlier in this article. - ## Related links - [Unit testing code coverage](unit-testing-code-coverage.md) From 55dc4ba1a447239f73c4a6246e7b732ca31849fa Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Sat, 22 Nov 2025 16:33:29 +0000 Subject: [PATCH 4/8] Update TUnit documentation for code coverage support and project creation --- .../testing/unit-testing-code-coverage.md | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/docs/core/testing/unit-testing-code-coverage.md b/docs/core/testing/unit-testing-code-coverage.md index 3ff6bf1dd536d..ef5e3caffbb67 100644 --- a/docs/core/testing/unit-testing-code-coverage.md +++ b/docs/core/testing/unit-testing-code-coverage.md @@ -297,33 +297,31 @@ After running this command, an HTML file represents the generated report. ## Using code coverage with TUnit -Code coverage tools like Coverlet and ReportGenerator work with TUnit projects. TUnit is built on Microsoft.Testing.Platform and supports the same code coverage workflows as other .NET testing frameworks. +TUnit is built on Microsoft.Testing.Platform and uses Microsoft.Testing.Extensions.CodeCoverage for code coverage. ### Creating a TUnit test project with code coverage -To create a TUnit test project with code coverage support, use the TUnit project template: +To create a TUnit test project with code coverage support: ```dotnetcli dotnet new install TUnit.Templates -dotnet new tunit -n TUnit.Coverlet.Test +dotnet new tunit -n TUnit.CodeCoverage.Test ``` Add the project reference to your class library: ```dotnetcli -dotnet add TUnit.Coverlet.Test\TUnit.Coverlet.Test.csproj reference Numbers\Numbers.csproj +dotnet add TUnit.CodeCoverage.Test\TUnit.CodeCoverage.Test.csproj reference Numbers\Numbers.csproj ``` -Add the Coverlet NuGet package (if using .NET 9 SDK or earlier, use `dotnet add package` instead): +Add the Microsoft.Testing.Extensions.CodeCoverage package: ```dotnetcli -cd TUnit.Coverlet.Test && dotnet package add coverlet.msbuild && cd .. +dotnet add TUnit.CodeCoverage.Test package Microsoft.Testing.Extensions.CodeCoverage ``` ### TUnit test example -TUnit tests use async/await syntax: - ```csharp using TUnit.Assertions; using TUnit.Assertions.Extensions; @@ -360,7 +358,7 @@ public class PrimeServiceTests ### Running code coverage with TUnit -Code coverage works the same way with TUnit as with other frameworks. Since TUnit requires Microsoft.Testing.Platform mode, ensure your `global.json` includes: +Since TUnit requires Microsoft.Testing.Platform mode, ensure your `global.json` includes: ```json { @@ -370,19 +368,19 @@ Code coverage works the same way with TUnit as with other frameworks. Since TUni } ``` -Run tests with code coverage using the same commands: +Run tests with code coverage: ```dotnetcli -dotnet test --collect:"XPlat Code Coverage" +dotnet test --coverage ``` -Or with MSBuild integration: +This generates a `.coverage` file in the `TestResults` directory. To generate reports in other formats: ```dotnetcli -dotnet test /p:CollectCoverage=true +dotnet test --coverage --coverage-output-format cobertura ``` -The generated coverage reports work with ReportGenerator just like xUnit, MSTest, or NUnit projects. For more information about TUnit, see [Unit testing C# with TUnit](unit-testing-csharp-with-tunit.md). +For more information about TUnit, see [Unit testing C# with TUnit](unit-testing-csharp-with-tunit.md). ## See also From 8b21a13add75f8b2f5591f8917824784c3fd29be Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Sat, 22 Nov 2025 16:38:00 +0000 Subject: [PATCH 5/8] Refactor documentation to simplify test project configuration details for TUnit --- docs/core/testing/unit-testing-csharp-with-tunit.md | 5 +---- docs/core/testing/unit-testing-fsharp-with-tunit.md | 11 ++--------- .../testing/unit-testing-visual-basic-with-tunit.md | 4 +--- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/docs/core/testing/unit-testing-csharp-with-tunit.md b/docs/core/testing/unit-testing-csharp-with-tunit.md index f7670b12d9605..00efb991a3221 100644 --- a/docs/core/testing/unit-testing-csharp-with-tunit.md +++ b/docs/core/testing/unit-testing-csharp-with-tunit.md @@ -85,10 +85,7 @@ The following instructions provide the steps to create the test solution. See [C dotnet new tunit -o PrimeService.Tests ``` - The preceding command creates the *PrimeService.Tests* project in the *PrimeService.Tests* directory. The test project uses [TUnit](https://tunit.dev/) as the test library. TUnit uses source generation for test discovery. The template configures the test runner by adding the following `` elements to the project file: - - * `TUnit` - * `TUnit.Engine` + The preceding command creates the *PrimeService.Tests* project in the *PrimeService.Tests* directory. The test project uses [TUnit](https://tunit.dev/) as the test library. TUnit uses source generation for test discovery. The template configures the test runner by adding the `TUnit` package to the project file. * Add the test project to the solution file by running the following command: diff --git a/docs/core/testing/unit-testing-fsharp-with-tunit.md b/docs/core/testing/unit-testing-fsharp-with-tunit.md index 39917e533d342..ad268d42e8a17 100644 --- a/docs/core/testing/unit-testing-fsharp-with-tunit.md +++ b/docs/core/testing/unit-testing-fsharp-with-tunit.md @@ -57,16 +57,9 @@ Install the TUnit project template: dotnet new install TUnit.Templates ``` -Make the *MathService.Tests* directory the current directory and create a new project using `dotnet new tunit -lang "F#"`. This creates a test project that uses TUnit as the test library. TUnit uses source generation for test discovery. The generated template configures the test runner in the *MathServiceTests.fsproj*: +Make the *MathService.Tests* directory the current directory and create a new project using `dotnet new tunit -lang "F#"`. This creates a test project that uses TUnit as the test library. TUnit uses source generation for test discovery. The generated template configures the test runner by adding the `TUnit` package to the *MathServiceTests.fsproj* file. -```xml - - - - -``` - -The test project requires other packages to create and run unit tests. `dotnet new` in the previous step added TUnit and the TUnit engine. Now, add the `MathService` class library as another dependency to the project. Use the `dotnet reference add` command: +The test project requires other packages to create and run unit tests. `dotnet new` in the previous step added TUnit. Now, add the `MathService` class library as another dependency to the project. Use the `dotnet reference add` command: ```dotnetcli dotnet reference add ../MathService/MathService.fsproj diff --git a/docs/core/testing/unit-testing-visual-basic-with-tunit.md b/docs/core/testing/unit-testing-visual-basic-with-tunit.md index a32389610dc42..de814013d51fd 100644 --- a/docs/core/testing/unit-testing-visual-basic-with-tunit.md +++ b/docs/core/testing/unit-testing-visual-basic-with-tunit.md @@ -89,9 +89,7 @@ The following instructions provide the steps to create the test solution. See [C * The preceding command: * Creates the *PrimeService.Tests* project in the *PrimeService.Tests* directory. The test project uses [TUnit](https://tunit.dev/) as the test library. * TUnit uses source generation for test discovery. - * Configures the test runner by adding the following `` elements to the project file: - * `TUnit` - * `TUnit.Engine` + * Configures the test runner by adding the `TUnit` package to the project file. * Add the test project to the solution file by running the following command: From a877e80d571e523ddd61630e5f2c7d02e25bc3fe Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Sat, 22 Nov 2025 16:40:11 +0000 Subject: [PATCH 6/8] Update TUnit documentation to clarify source generation and test discovery details --- docs/core/testing/index.md | 2 +- docs/core/testing/unit-testing-fsharp-with-tunit.md | 4 ++-- docs/core/testing/unit-testing-visual-basic-with-tunit.md | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/core/testing/index.md b/docs/core/testing/index.md index 06341582d9523..590cc98b03415 100644 --- a/docs/core/testing/index.md +++ b/docs/core/testing/index.md @@ -74,7 +74,7 @@ For more information, see the following resources: #### TUnit -[TUnit](https://tunit.dev/) is a testing framework for .NET that is built entirely on top of Microsoft.Testing.Platform and doesn't support VSTest. TUnit uses source generation for test discovery and runs tests in parallel by default. +[TUnit](https://tunit.dev/) is a testing framework for .NET that is built entirely on top of Microsoft.Testing.Platform and doesn't support VSTest. TUnit uses source generation for C# test discovery and is Native AOT compatible. Tests run in parallel by default. For more information, see the following resources: diff --git a/docs/core/testing/unit-testing-fsharp-with-tunit.md b/docs/core/testing/unit-testing-fsharp-with-tunit.md index ad268d42e8a17..a18c4a8171fa4 100644 --- a/docs/core/testing/unit-testing-fsharp-with-tunit.md +++ b/docs/core/testing/unit-testing-fsharp-with-tunit.md @@ -57,7 +57,7 @@ Install the TUnit project template: dotnet new install TUnit.Templates ``` -Make the *MathService.Tests* directory the current directory and create a new project using `dotnet new tunit -lang "F#"`. This creates a test project that uses TUnit as the test library. TUnit uses source generation for test discovery. The generated template configures the test runner by adding the `TUnit` package to the *MathServiceTests.fsproj* file. +Make the *MathService.Tests* directory the current directory and create a new project using `dotnet new tunit -lang "F#"`. This creates a test project that uses TUnit as the test library. The generated template configures the test runner by adding the `TUnit` package to the *MathServiceTests.fsproj* file. The test project requires other packages to create and run unit tests. `dotnet new` in the previous step added TUnit. Now, add the `MathService` class library as another dependency to the project. Use the `dotnet reference add` command: @@ -121,7 +121,7 @@ let ``Fail every time`` () = } ``` -The `[]` attribute marks a method as a test that's run by the test runner. TUnit uses source generation to discover tests at compile time. +The `[]` attribute marks a method as a test that's run by the test runner. From the *unit-testing-with-fsharp* directory, execute `dotnet test` to build the tests and the class library and then run the tests. The TUnit test runner uses Microsoft.Testing.Platform to execute the tests. diff --git a/docs/core/testing/unit-testing-visual-basic-with-tunit.md b/docs/core/testing/unit-testing-visual-basic-with-tunit.md index de814013d51fd..4b500c5cff59b 100644 --- a/docs/core/testing/unit-testing-visual-basic-with-tunit.md +++ b/docs/core/testing/unit-testing-visual-basic-with-tunit.md @@ -88,7 +88,6 @@ The following instructions provide the steps to create the test solution. See [C * The preceding command: * Creates the *PrimeService.Tests* project in the *PrimeService.Tests* directory. The test project uses [TUnit](https://tunit.dev/) as the test library. - * TUnit uses source generation for test discovery. * Configures the test runner by adding the `TUnit` package to the project file. * Add the test project to the solution file by running the following command: @@ -174,7 +173,7 @@ Namespace PrimeService.Tests End Namespace ``` -The `[Test]` attribute marks a method as a test that's run by the test runner. TUnit uses source generation to discover tests at compile time. +The `[Test]` attribute marks a method as a test that's run by the test runner. From the *PrimeService.Tests* folder, run `dotnet test`. The [dotnet test](../tools/dotnet-test.md) command builds both projects and runs the tests. The TUnit test runner uses Microsoft.Testing.Platform to execute the tests. From 95c9bb7ecb3fdc52da5467cc835dc26871fe2ace Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Sat, 22 Nov 2025 16:45:54 +0000 Subject: [PATCH 7/8] Update docs/core/testing/unit-testing-csharp-with-tunit.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/core/testing/unit-testing-csharp-with-tunit.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/core/testing/unit-testing-csharp-with-tunit.md b/docs/core/testing/unit-testing-csharp-with-tunit.md index 00efb991a3221..9927a8dbeb2d1 100644 --- a/docs/core/testing/unit-testing-csharp-with-tunit.md +++ b/docs/core/testing/unit-testing-csharp-with-tunit.md @@ -46,7 +46,7 @@ The following instructions provide the steps to create the test solution. See [C dotnet new classlib -o PrimeService ``` - The [`dotnet new classlib`](../tools/dotnet-new.md) command creates a new class library project in the *PrimeService* folder. The new class library will contain the code to be tested. + The [`dotnet new classlib`](../tools/dotnet-new.md) command creates a new class library project in the *PrimeService* folder. The new class library will contain the code to be tested. * Rename *Class1.cs* to *PrimeService.cs*. * Replace the code in *PrimeService.cs* with the following code: From 29a5730ec3ae15f9569a4c42b0d7666f7c1b5f0f Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Sat, 22 Nov 2025 16:46:06 +0000 Subject: [PATCH 8/8] Update docs/core/testing/unit-testing-visual-basic-with-tunit.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/core/testing/unit-testing-visual-basic-with-tunit.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/core/testing/unit-testing-visual-basic-with-tunit.md b/docs/core/testing/unit-testing-visual-basic-with-tunit.md index 4b500c5cff59b..68dc5803e5ffb 100644 --- a/docs/core/testing/unit-testing-visual-basic-with-tunit.md +++ b/docs/core/testing/unit-testing-visual-basic-with-tunit.md @@ -46,7 +46,7 @@ The following instructions provide the steps to create the test solution. See [C dotnet new classlib -o PrimeService -lang VB ``` - The [`dotnet new classlib`](../tools/dotnet-new.md) command creates a new class library project in the *PrimeService* folder. The new class library will contain the code to be tested. + The [`dotnet new classlib`](../tools/dotnet-new.md) command creates a new class library project in the *PrimeService* folder. The new class library will contain the code to be tested. * Rename *Class1.vb* to *PrimeService.vb*. * Replace the code in *PrimeService.vb* with the following code: