You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Document TestingPlatformBuilderHook auto-registration for MTP extension authors (#54122)
* Document TestingPlatformBuilderHook auto-registration for MTP extension authors
Addresses microsoft/testfx#5552 by adding a new section to the MTP extensions architecture article that explains:
- How extension authors expose a TestingPlatformBuilderHook class and ship a buildMultiTargeting props file so consumers don't need manual registration.
- The required AddExtensions(ITestApplicationBuilder, string[]) method signature and the required MSBuild item metadata (Include, DisplayName, TypeFullName).
- That the Include GUID is a freshly generated, random identifier that must not be reused from any other extension (with an IMPORTANT callout) and is distinct from IExtension.Uid.
- How to verify the hook is wired up by inspecting the generated SelfRegisteredExtensions.g.cs file.
Also adds a TIP at the top of the Extensibility points section pointing readers to the new section, and bumps ms.date.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copy file name to clipboardExpand all lines: docs/core/testing/microsoft-testing-platform-architecture-extensions.md
+91-1Lines changed: 91 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,7 +3,7 @@ title: Build extensions for Microsoft.Testing.Platform (MTP)
3
3
description: Learn how to create in-process and out-of-process extensions for Microsoft.Testing.Platform (MTP).
4
4
author: MarcoRossignoli
5
5
ms.author: mrossignoli
6
-
ms.date: 02/24/2026
6
+
ms.date: 06/02/2026
7
7
ai-usage: ai-assisted
8
8
---
9
9
@@ -17,6 +17,9 @@ For the full extension point summary and in-process/out-of-process concepts, see
17
17
18
18
The testing platform provides additional extensibility points that allow you to customize the behavior of the platform and the test framework. These extensibility points are optional and can be used to enhance the testing experience.
19
19
20
+
> [!TIP]
21
+
> Each extension shown in this article includes a manual registration snippet (for example, `builder.TestHost.AddDataConsumer(...)`). If you're shipping your extension as a NuGet package, you can let consumers skip the manual call by exposing a `TestingPlatformBuilderHook` and a small MSBuild props file. The auto-generated entry point will then invoke your hook automatically. For details, see [Auto-register your extension with `TestingPlatformBuilderHook`](#auto-register-your-extension-with-testingplatformbuilderhook).
22
+
20
23
### The `ICommandLineOptionsProvider` extensions
21
24
22
25
> [!NOTE]
@@ -499,6 +502,93 @@ The `ITestHostProcessInformation` interface provides the following details:
499
502
*`ExitCode`: The exit code of the process. This value is only available within the `OnTestHostProcessExitedAsync` method. Attempting to access it within the `OnTestHostProcessStartedAsync` method will result in an exception.
500
503
*`HasExitedGracefully`: A boolean value indicating whether the test host has crashed. If true, it signifies that the test host did not exit gracefully.
501
504
505
+
## Auto-register your extension with `TestingPlatformBuilderHook`
506
+
507
+
Every preceding extension section shows a *manual* registration call (for example, `builder.TestHost.AddDataConsumer(...)`). Asking consumers to edit their `Main` method is a poor onboarding experience. The [`Microsoft.Testing.Platform.MSBuild`](https://www.nuget.org/packages/Microsoft.Testing.Platform.MSBuild) package solves this by generating a `SelfRegisteredExtensions.AddSelfRegisteredExtensions(builder, args)` method that runs from the autogenerated entry point. To plug your extension into that generated method, ship two artifacts in your NuGet package:
508
+
509
+
- A public static `TestingPlatformBuilderHook` class with an `AddExtensions` method that registers your extension.
510
+
- An MSBuild props file that declares a `<TestingPlatformBuilderHook>` item pointing at that class.
511
+
512
+
When a consumer installs your package, the MSBuild integration picks up the item and generates the call into your hook, and your extension is registered with no code changes on the consumer side.
513
+
514
+
> [!NOTE]
515
+
> Auto-registration only works when the consumer has `Microsoft.Testing.Platform.MSBuild` in their project (it's included transitively by MSTest, NUnit, and xUnit runners) and hasn't opted out by setting `<GenerateTestingPlatformEntryPoint>false</GenerateTestingPlatformEntryPoint>`. Consumers who disable the autogenerated entry point still need to call your manual registration API from their `Main` method.
516
+
517
+
### Create the hook class
518
+
519
+
Add a `public static class TestingPlatformBuilderHook` in your extension assembly with an `AddExtensions(ITestApplicationBuilder, string[])` method that performs the same registration users would otherwise call manually:
The class name doesn't have to be `TestingPlatformBuilderHook` — the MSBuild item points at it by full type name — but using that name keeps your code consistent with the in-box extensions like `Microsoft.Testing.Extensions.Retry` and `Microsoft.Testing.Extensions.HotReload`.
534
+
535
+
The method must:
536
+
537
+
* Be `public static`.
538
+
* Have a first parameter of type `Microsoft.Testing.Platform.Builder.ITestApplicationBuilder`.
539
+
* Have a second parameter of type `string[]` (the command-line arguments passed to the test host). You can ignore it if your extension doesn't need it.
540
+
* Return `void`.
541
+
542
+
### Declare the MSBuild item
543
+
544
+
Ship a props file under `buildMultiTargeting/<PackageId>.props` inside your NuGet package. Declare a `<TestingPlatformBuilderHook>` item that points the MSBuild task at your hook class:
*`Include`: A GUID that uniquely identifies your hook. See [The `Include` GUID is a random identifier](#the-include-guid-is-a-random-identifier).
560
+
*`DisplayName`: The friendly name shown in MSBuild diagnostic messages when the entry point is generated. Use your package or extension name.
561
+
*`TypeFullName`: The fully qualified name of the `TestingPlatformBuilderHook` class you created previously. The MSBuild task uses this to emit `global::Contoso.MyExtension.TestingPlatformBuilderHook.AddExtensions(builder, args);` into the generated entry point.
562
+
563
+
### The `Include` GUID is a random identifier
564
+
565
+
The GUID in the `Include` attribute is **not** the same as your extension's [`IExtension.Uid`](./microsoft-testing-platform-architecture.md#the-iextension-interface). It's a registration identifier used by the MSBuild task to deduplicate hooks across NuGet references and (in a few well-known cases) to order them.
566
+
567
+
When you author a new extension, generate a brand new GUID and hard-code it in your props file. Some ways to generate one:
568
+
569
+
* PowerShell: `[guid]::NewGuid()`
570
+
* Visual Studio: **Tools** > **Create GUID**
571
+
*`uuidgen` on Linux and macOS
572
+
573
+
> [!IMPORTANT]
574
+
> Never copy a GUID from another extension's props file (whether shipped by Microsoft or by a third party). Two extensions that share the same `Include` value are treated as duplicates: only one hook is invoked, so your extension silently fails to register.
575
+
576
+
> [!NOTE]
577
+
> Once you ship a GUID, treat it as permanent. Changing it in a later release is harmless on its own, but reusing the *old* value for a different hook in a future package version can confuse consumers who have both versions in their dependency graph during an upgrade.
578
+
579
+
### Verify the hook is wired up
580
+
581
+
After installing your package in a test project that uses `Microsoft.Testing.Platform.MSBuild`, build the project and inspect the generated `SelfRegisteredExtensions.g.cs` file under `obj/<Configuration>/<TargetFramework>/`. You should see a call into your hook, for example:
If the call is missing, double-check that the props file is packaged under `buildMultiTargeting/` (not `build/`) inside the `.nupkg`, that `DisplayName` and `TypeFullName` metadata are present, and that the consumer hasn't set `<GenerateTestingPlatformEntryPoint>false</GenerateTestingPlatformEntryPoint>`.
591
+
502
592
## Extensions execution order
503
593
504
594
The testing platform consists of a [testing framework](./microsoft-testing-platform-architecture-test-framework.md#test-framework-extension) and any number of extensions that can operate [*in-process*](./microsoft-testing-platform-architecture.md#in-process-vs-out-of-process-extensions) or [*out-of-process*](./microsoft-testing-platform-architecture.md#in-process-vs-out-of-process-extensions). This document outlines the **sequence of calls** to all potential extensibility points to provide clarity on when a feature is anticipated to be invoked:
0 commit comments