Skip to content

Commit a3c6fc6

Browse files
committed
Add /releases/v3/3.2.0, /releases/analyzers/1.25.0, and /docs/getting-started/v3/custom-test-class-construction
1 parent 91148a7 commit a3c6fc6

File tree

8 files changed

+197
-98
lines changed

8 files changed

+197
-98
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
---
2+
title: Custom Test Class Construction
3+
title-version: 2025 November 2
4+
---
5+
6+
As of version `3.2.0`, we are now supporting a way to override the default test class construction behavior.
7+
8+
## Why Override?
9+
10+
By default, test class constructor arguments can be:
11+
12+
* [`ITestContextAccessor`](https://github.com/xunit/xunit/blob/9e422989c1a48ce35b821566c12d01ea418018bc/src/xunit.v3.core/ITestContextAccessor.cs)
13+
* [`ITestOutputHelper`](https://github.com/xunit/xunit/blob/9e422989c1a48ce35b821566c12d01ea418018bc/src/xunit.v3.core/ITestOutputHelper.cs)
14+
* Fixture value (class fixture, collection fixture, or assembly fixture)
15+
16+
Any constructor argument which cannot be resolved will generate an error when running the test:
17+
18+
> `The following constructor parameters did not have matching fixture data`
19+
20+
Some developers may want to use additional logic to resolve these missing constructor arguments. One common scenario might be to allow a dependency injection system to resolve those arguments; for example, to give tests access to services registered that the production code will be using.
21+
22+
Now they can provide an alternative construction system for test classes.
23+
24+
## Implementing `ITypeActivator`
25+
26+
The new [`ITypeActivator` interface](https://github.com/xunit/xunit/blob/9e422989c1a48ce35b821566c12d01ea418018bc/src/xunit.v3.core/Abstractions/Framework/ITypeActivator.cs) contains a single method:
27+
28+
```csharp
29+
object CreateInstance(
30+
ConstructorInfo constructor,
31+
object?[]? arguments,
32+
Func<Type, IReadOnlyCollection<ParameterInfo>, string> missingArgumentMessageFormatter);
33+
```
34+
35+
The activator is given the constructor that the test framework has selected, along with all the arguments that it could collect. Any argument value which could not be found will be represented in the array by [`Missing.Value`](https://learn.microsoft.com/dotnet/api/system.reflection.missing.value).
36+
37+
The activator should resolve all the missing values and then constructor and return the object. If one or more missing values cannot be resolved, it is expected that the activator will throw an instance of [`TestPipelineException`](https://github.com/xunit/xunit/blob/9e422989c1a48ce35b821566c12d01ea418018bc/src/xunit.v3.core/Exceptions/TestPipelineException.cs). A `missingArgumentMessageFormatter` function is provided to the activator, which can be called with (a) the `Type` that's being created, and (b) the list of `ParameterInfo` that are missing values (this allows the caller to express the context in which the creation failed; this is where the message above is provided by the test framework).
38+
39+
The default type activator cannot resolve any unknown parameters, so its implementation looks like this:
40+
41+
```csharp
42+
object ITypeActivator.CreateInstance(
43+
ConstructorInfo constructor,
44+
object?[]? arguments,
45+
Func<Type, IReadOnlyCollection<ParameterInfo>, string> missingArgumentMessageFormatter)
46+
{
47+
if (constructor is null)
48+
throw new ArgumentNullException(nameof(constructor));
49+
if (missingArgumentMessageFormatter is null)
50+
throw new ArgumentNullException(nameof(missingArgumentMessageFormatter));
51+
52+
var type =
53+
constructor.ReflectedType
54+
?? constructor.DeclaringType
55+
?? throw new ArgumentException("Untyped constructors are not permitted", nameof(constructor));
56+
57+
if (arguments is not null)
58+
{
59+
var parameters = constructor.GetParameters();
60+
if (parameters.Length != arguments.Length)
61+
throw new TestPipelineException(
62+
string.Format(
63+
CultureInfo.CurrentCulture,
64+
"Cannot create type '{0}' due to parameter count mismatch (needed {1}, got {2})",
65+
type.FullName ?? type.Name,
66+
parameters.Length,
67+
arguments.Length
68+
)
69+
);
70+
71+
var missingArguments =
72+
arguments
73+
.Select((a, idx) => a is Missing ? parameters[idx] : null)
74+
.WhereNotNull()
75+
.CastOrToReadOnlyCollection();
76+
77+
if (missingArguments.Count != 0)
78+
throw new TestPipelineException(missingArgumentMessageFormatter(type, missingArguments));
79+
}
80+
81+
return constructor.Invoke(arguments);
82+
}
83+
```
84+
85+
## Registering your type activator
86+
87+
It is assumed that things like dependency injection containers will be created by way of an early registration system like [`ITestPipelineStartup`](https://github.com/xunit/xunit/blob/9e422989c1a48ce35b821566c12d01ea418018bc/src/xunit.v3.core/Abstractions/Framework/ITestPipelineStartup.cs).
88+
89+
Once the container is fully configured and the type activator has been created, it is registered by calling:
90+
91+
```csharp
92+
Xunit.v3.TypeActivator.Current = MY_TYPE_ACTIVATOR_INSTANCE;
93+
```
94+
95+
It is assumed that a type activator will be created and registered once during the pipeline startup and left in place for the duration of the test assembly lifetime.

site/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,9 @@ xUnit.net v3 In-Process Runner v2.0.3+216a74a292 (64-bit .NET 8.0.17)
255255
{: .latest }
256256
| | Stable | Prerelease
257257
| --------------------- | -------------------------------------------------- | ----------
258-
| Core Framework v3 | [3.1.0](/releases/v3/3.1.0){: .release } | _None_
258+
| Core Framework v3 | [3.2.0](/releases/v3/3.2.0){: .release } | _None_
259259
| Core Framework v2 | [2.9.3](/releases/v2/2.9.3){: .release } | _None_
260-
| Analyzers | [1.24.0](/releases/analyzers/1.24.0){: .release } | _None_
260+
| Analyzers | [1.25.0](/releases/analyzers/1.25.0){: .release } | _None_
261261
| Visual Studio adapter | [3.1.5](/releases/visualstudio/3.1.5){: .release } | _None_
262262

263263
_For older release notes, see the [full release notes list](/releases/)._

site/releases/analyzers/1.25.0.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
title: Analyzers 1.25.0
3+
title-version: 2025 November 2
4+
logo-title: true
5+
css: release-notes.css
6+
---
7+
8+
Today, we're shipping two new releases:
9+
10+
* xUnit.net Core Framework v3 `3.2.0` ([release notes](/releases/v3/3.2.0))
11+
* **xUnit.net Analyzers `1.25.0`**
12+
13+
It's been 3½ months since the release of [`1.24.0`](/releases/analyzers/1.24.0).
14+
15+
As always, we'd like to thank all the users who contributed to the success of xUnit.net through usage, feedback, and code. 🎉
16+
17+
## Release Notes
18+
19+
These release notes are a comprehensive list of changes from `1.24.0` to `1.25.0`.
20+
21+
### Usage Analyzers
22+
23+
* We have added [xUnit1053](/xunit.analyzers/rules/xUnit1053) to point out when reference `[MemberData]` has not been initialized. Failing to initialize the member data appropriate results in zero data rows during discovery/execution. [xunit/xunit#12589](https://github.com/xunit/xunit/issues/1258){: .issue-link }
24+
25+
### Extensibility Analyzers
26+
27+
* We have updated [xUnit3001](/xunit.analyzers/rules/xUnit2002) to not trigger when the class in question is abstract. Public parameterless constructors are only required for instantiable classes covered by xUnit3001. [xunit/xunit#3169](https://github.com/xunit/xunit/issues/3169){: .issue-link }

site/releases/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Continuous integration packages are available on [Feedz.io](https://feedz.io/org
1212

1313
| NuGet&nbsp;Package: | [`xunit.v3`](https://www.nuget.org/packages/xunit.v3) and friends
1414
| ------------------- | -----
15-
| **Releases:** | [3.1.0](/releases/v3/3.1.0){: .release } [3.0.1](/releases/v3/3.0.1){: .release } [3.0.0](/releases/v3/3.0.0){: .release } [2.0.3](/releases/v3/2.0.3){: .release } [2.0.2](/releases/v3/2.0.2){: .release } [2.0.1](/releases/v3/2.0.1){: .release } [2.0.0](/releases/v3/2.0.0){: .release } [1.1.0](/releases/v3/1.1.0){: .release } [1.0.1](/releases/v3/1.0.1){: .release } [1.0.0](/releases/v3/1.0.0){: .release }
15+
| **Releases:** | [3.2.0](/releases/v3/3.2.0){: .release } [3.1.0](/releases/v3/3.1.0){: .release } [3.0.1](/releases/v3/3.0.1){: .release } [3.0.0](/releases/v3/3.0.0){: .release } [2.0.3](/releases/v3/2.0.3){: .release } [2.0.2](/releases/v3/2.0.2){: .release } [2.0.1](/releases/v3/2.0.1){: .release } [2.0.0](/releases/v3/2.0.0){: .release } [1.1.0](/releases/v3/1.1.0){: .release } [1.0.1](/releases/v3/1.0.1){: .release } [1.0.0](/releases/v3/1.0.0){: .release }
1616
| **Prereleases:** | _None_
1717

1818
## Core Framework v2
@@ -26,7 +26,7 @@ Continuous integration packages are available on [Feedz.io](https://feedz.io/org
2626

2727
| NuGet&nbsp;Package: | [`xunit.analyzers`](https://www.nuget.org/packages/xunit.analyzers)
2828
| ------------------- | -----
29-
| **Releases:** | [1.24.0](/releases/analyzers/1.24.0){: .release } [1.23.0](/releases/analyzers/1.23.0){: .release } [1.22.0](/releases/analyzers/1.22.0){: .release } [1.21.0](/releases/analyzers/1.21.0){: .release } [1.20.0](/releases/analyzers/1.20.0){: .release } [1.19.0](/releases/analyzers/1.19.0){: .release } [1.18.0](/releases/analyzers/1.18.0){: .release } [1.17.0](/releases/analyzers/1.17.0){: .release } [1.16.0](/releases/analyzers/1.16.0){: .release } [1.15.0](/releases/analyzers/1.15.0){: .release } [1.14.0](/releases/analyzers/1.14.0){: .release } [1.13.0](/releases/analyzers/1.13.0){: .release } [1.12.0](/releases/analyzers/1.12.0){: .release } [1.11.0](/releases/analyzers/1.11.0){: .release } [1.10.0](/releases/analyzers/1.10.0){: .release } [1.9.0](/releases/analyzers/1.9.0){: .release } [1.8.0](/releases/analyzers/1.8.0){: .release } [1.7.0](/releases/analyzers/1.7.0){: .release } [1.6.0](/releases/analyzers/1.6.0){: .release } [1.5.0](/releases/analyzers/1.5.0){: .release } [1.4.0](/releases/analyzers/1.4.0){: .release } [1.3.0](/releases/analyzers/1.3.0){: .release } [1.2.0](/releases/analyzers/1.2.0){: .release } [1.1.0](/releases/analyzers/1.1.0){: .release } [1.0.0](/releases/analyzers/1.0.0){: .release }
29+
| **Releases:** | [1.25.0](/releases/analyzers/1.25.0){: .release } [1.24.0](/releases/analyzers/1.24.0){: .release } [1.23.0](/releases/analyzers/1.23.0){: .release } [1.22.0](/releases/analyzers/1.22.0){: .release } [1.21.0](/releases/analyzers/1.21.0){: .release } [1.20.0](/releases/analyzers/1.20.0){: .release } [1.19.0](/releases/analyzers/1.19.0){: .release } [1.18.0](/releases/analyzers/1.18.0){: .release } [1.17.0](/releases/analyzers/1.17.0){: .release } [1.16.0](/releases/analyzers/1.16.0){: .release } [1.15.0](/releases/analyzers/1.15.0){: .release } [1.14.0](/releases/analyzers/1.14.0){: .release } [1.13.0](/releases/analyzers/1.13.0){: .release } [1.12.0](/releases/analyzers/1.12.0){: .release } [1.11.0](/releases/analyzers/1.11.0){: .release } [1.10.0](/releases/analyzers/1.10.0){: .release } [1.9.0](/releases/analyzers/1.9.0){: .release } [1.8.0](/releases/analyzers/1.8.0){: .release } [1.7.0](/releases/analyzers/1.7.0){: .release } [1.6.0](/releases/analyzers/1.6.0){: .release } [1.5.0](/releases/analyzers/1.5.0){: .release } [1.4.0](/releases/analyzers/1.4.0){: .release } [1.3.0](/releases/analyzers/1.3.0){: .release } [1.2.0](/releases/analyzers/1.2.0){: .release } [1.1.0](/releases/analyzers/1.1.0){: .release } [1.0.0](/releases/analyzers/1.0.0){: .release }
3030
| **Prereleases:** | _None_
3131

3232
## Visual Studio adapter

site/releases/v3/3.2.0-pre.10.md

Lines changed: 1 addition & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,3 @@
11
---
2-
title: Core Framework v3 3.2.0-pre.10
3-
title-version: 2025 September 15
4-
logo-title: true
5-
css: release-notes.css
2+
redirect_url: 3.2.0
63
---
7-
8-
Today, we're shipping one new prerelease:
9-
10-
* **xUnit.net Core Framework v3 `3.2.0-pre.10`**
11-
12-
As always, we'd like to thank all the users who contributed to the success of xUnit.net through usage, feedback, and code. 🎉
13-
14-
## Microsoft Testing Platform v2
15-
16-
This prerelease build is released for users to be able to use preview builds of Microsoft Testing Platform v2. It is anticipated that Microsoft Testing Platform v2 RTM will ship sometime around the same time as .NET 10 SDK RTM. Our plan is for xUnit.net v3 3.2.0 RTM to also ship around the same time.
17-
18-
### Updated Templates with `net10.0` Support
19-
20-
The version of the `xunit.v3.templates` NuGet package included in this build now supports targeting .NET 10 (`net10.0`). You can set the framework for `dotnet new` templates by passing `--framework <tfm>` to the `dotnet new` command line.
21-
22-
* The default framework for `xunit3` will remain `net8.0` until it reaches end of life
23-
* The default framework for `xunit3-extension` will remain `netstandard2.0` indefinitely
24-
25-
### Support for `global.json` with .NET 10 SDK
26-
27-
When unfolding the `xunit3` template, it will create or update the `global.json` file in the solution folder root. With .NET 10 SDK, the MSBuild property `TestingPlatformDotnetTestSupport` has been obsoleted in favor of expressing the runner in `global.json` to let `dotnet test` know whether you plan to use VSTest mode or MTP mode. Additionally, MTP v2 test projects (when run with .NET 10 SDK) can only be run in MTP mode; VSTest mode is no longer supported. This will include all builds of xUnit.net v3 3.2.0 or later.
28-
29-
> [!NOTE]
30-
> The requirement to use `global.json` with .NET 10 SDK with MTP v2 is true regardless of the target framework that your tests target (.NET 8+ or .NET Framework 4.7.2+). If you try to run `dotnet test` against an MTP v2 enabled test project from .NET 10 SDK without creating the required `global.json` configuration element first, the system will report an error, which includes a link to Microsoft documentation.
31-
>
32-
> We only attempt to create/update `global.json` when you target `net10.0` because our testing has shown that the template action we're relying on does not work with .NET 8 SDK. Unfortunately, templates do not know what version of the .NET SDK you're using, only the version of the framework you're _choosing to target_ so this was the next best thing we could do, to prevent issues for users who are still running the LTS SDK (.NET 8).
33-
34-
To manually create the `global.json` update, please create or update your `global.json` file to include this entry:
35-
36-
```json
37-
{
38-
"test": {
39-
"runner": "Microsoft.Testing.Platform"
40-
}
41-
}
42-
```
43-
44-
For more information about `global.json` and `dotnet test`, please see the [dotnet test](https://learn.microsoft.com/dotnet/core/tools/dotnet-test) documentation.
45-
46-
#### Using .NET 10 SDK prior to RC2
47-
48-
As of the writing of this release note, .NET SDK RC1 ships support for `dotnet.config` rather than `global.json`, and as such the linked documentation site above talks about `dotnet.config`. This will be replaced with the `global.json` support mentioned above in .NET SDK RC2, and presumably the documentation page will also be updated when RC2 is shipped. Once this has happened, we will update this documentation page as well.
49-
50-
In the meantime, users who are using versions of .NET 10 SDK prior to RC2 will need to hand-create a `dotnet.config` file in their solution folder root, with the following contents:
51-
52-
```ini
53-
[dotnet.test.runner]
54-
name = "Microsoft.Testing.Platform"
55-
```
56-
57-
You can (and should) leave the `global.json` changes in place, as they will be required when you upgrade to RC2 (and will be harmlessly ignored with RC1).
58-
59-
## Release Notes
60-
61-
An RTM release for 3.1.0 is currently planned within the next couple weeks, and this preview release includes features from that codebase as well. Those new features are listed below.
62-
63-
### Core Framework
64-
65-
* We have added a new `XunitSerializer<T>` class which can be used as a base class when implementing `IXunitSerializer` for one type. This adds type safety to the APIs (giving instances of `T` to `Serialize` and `IsSerializable`, and expecting an instance of `T` from `Deserialize`), as well as providing some boilerplate logic to ensure this type safety at runtime. This also includes automatic support for arrays of values, meaning that `IsSerializable` will be called repeatedly with the instances of values in the array rather than being called just once with the array itself (as is the case with `IXunitSerializer.IsSerializable`).
66-
67-
* We have enhanced the support for event source events produced by xUnit.net. In addition to `TestStart` and `TestStop` events, we have also added `TestAssemblyStart`, `TestAssemblyStop`, `TestCollectionStart`, `TestCollectionStop`, `TestClassStart`, `TestClassStop`, `TestMethodStart`, `TestMethodStop`, `TestCaseStart`, and `TestCaseStop`. We have also added a new `result` value to `TestStop` to indicate what the final test result was. [xunit/xunit#3386](https://github.com/xunit/xunit/issues/3386){: .issue-link }
68-
69-
* **BUG:** We have fixed an issue where `IRunnerReporterMessageHandler.DisposeAsync` was not being called. [xunit/xunit#3385](https://github.com/xunit/xunit/issues/3385){: .issue-link }
70-
71-
* **BUG:** We have fixed an issue in `ConsoleRunnerInProcess` (which is primarily used by NCrunch) where we were causing an extra unnecessary discovery phase when trying to run individual tests. This should improve individual test execution performance.
72-
73-
* **BUG:** We have fixed an issue where we could mistakenly try to create fixture instances when we know ahead of time that all tests using those fixture instances would be skipped. [xunit/xunit#3371](https://github.com/xunit/xunit/issues/3371){: .issue-link }
74-
75-
### Assertion Library
76-
77-
* **BUG:** We have fixed an issue with `Assert.EquivalentWithExclusions` with the way that collections are handled. The expression for collections must now include `[]` at the end of a collection property/field name. For example, the expression `MyCollection[].MyProperty` will exclude `MyProperty` when comparing values inside the `MyCollection` collection. For more information, see the attached issue. [xunit/xunit#3394](https://github.com/xunit/xunit/issues/3394){: .issue-link }
78-
79-
### Runners
80-
81-
* We have updated the Azure DevOps runner reporter to use the latest versions of the APIs. [xunit/xunit#3376](https://github.com/xunit/xunit/issues/3376){: .issue-link }
82-
83-
* We have updated the default runner reporter to report the test assembly's unique ID in the "Finished" message displayed on-screen, rather than in the summary, due to the fact that some runners (like `dotnet test`) did not display the summary. [xunit/xunit#3365](https://github.com/xunit/xunit/issues/3365){: .issue-link }
84-
85-
* We have updated the `-list full` output from the in-process console runner to include the test case unique ID of each discovered test (this is available in both human-readable form as well as JSON form via `-list full/json`). We have also added a `-id` switch that allows the user to run a test case by unique ID. This complements the existing MTP command line switch (`--filter-uid`) which also allows running a test case by unique ID. [xunit/xunit#3179](https://github.com/xunit/xunit/issues/3179){: .issue-link }
86-
87-
* **BUG:** We have fixed the output of the JUnit report to conform to the JUnit 4 XSD. This changed the skip counter attribute name in `testsuites` from `skipped` to `disabled`, and removes the attachment, traits, and warning nodes that were previously (illegally) reported under the `testsuite`. [xunit/xunit#3393](https://github.com/xunit/xunit/issues/3393){: .issue-link }
88-
89-
### Runner Utility
90-
91-
* We have added `XunitProjectAssembly.TestCaseIDsToRun`, which allows runner authors to specify test cases they wish to run by ID. This supplements the existing `XunitProjectAssembly.TestCasesToRun`, which allows runner authors to specify test cases they wish to run by providing the serialized test case. This is used by the new `-id` console runner switch mentioned above. [xunit/xunit#3179](https://github.com/xunit/xunit/issues/3179){: .issue-link }
92-
93-
### Microsoft Testing Platform
94-
95-
* We will only pre-enumerate theory data rows by default when running in Test Explorer now. Previously we would pre-enumerate theory data rows by default when running in `dotnet test`, but we are skipping this now by default (as a performance optimization). You can as always [force pre-enumeration through configuration](/docs/config-xunit-runner-json#preEnumerateTheories).

site/releases/v3/3.2.0-pre.5.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
---
2-
redirect_url: 3.2.0-pre.10
2+
redirect_url: 3.2.0
33
---

0 commit comments

Comments
 (0)