From 58e7bc587b85f16e18c8eb00b50c412c5c150290 Mon Sep 17 00:00:00 2001 From: Jurriaan Wijnberg Date: Wed, 12 Nov 2025 09:16:48 +0100 Subject: [PATCH 1/3] Upgrade to .NET 10 and modernize codebase Updated the project to target .NET 10.0, including upgrading the .NET SDK to version 9.0.100 and setting `` to `latest`. Updated MSTest dependencies to version 4.0.2 and refactored `FeatureTestMethodAttribute` to support asynchronous execution (`ExecuteAsync`). Replaced legacy assertions with modern equivalents (`Assert.HasCount`, `Assert.IsTrue`, `Assert.IsFalse`) and updated test methods to use `async Task`. Simplified the `IFeatureService` interface by removing redundant `public` modifiers. Introduced a helper class for static members in `FeatureTestMethodAttribute` and addressed MSTest analyzer warnings. Removed BOM from `IFeatureService.cs` and performed general code cleanup to align with modern C# conventions. --- Directory.Build.props | 2 +- global.json | 14 +- src/Directory.Build.props | 2 +- .../FeatureSwitches.MSTest.csproj | 2 +- .../FeatureTestMethodAttribute.cs | 46 +++++-- .../FeatureSwitches.ServiceCollection.csproj | 4 +- src/FeatureSwitches/IFeatureService.cs | 6 +- test/Directory.Build.props | 2 +- .../FeatureSwitches.Test.csproj | 2 +- .../FeatureServiceIntegrationTest.cs | 6 +- .../MSTest/FeatureTestMethodAttributeTest.cs | 130 +++++++++--------- 11 files changed, 118 insertions(+), 98 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index e0708d6..b8a1423 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,7 +4,7 @@ true - 13.0 + latest enable enable true diff --git a/global.json b/global.json index 01429f6..21b3bf1 100644 --- a/global.json +++ b/global.json @@ -1,9 +1,9 @@ { - "sdk": { - "version": "9.0.100", - "rollForward": "latestFeature" - }, - "msbuild-sdks": { - "MSTest.Sdk": "3.6.3" - } + "sdk": { + "version": "9.0.100", + "rollForward": "latestMajor" + }, + "msbuild-sdks": { + "MSTest.Sdk": "4.0.2" + } } diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 135bf0b..b87d44b 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ - net9.0 + net10.0 diff --git a/src/FeatureSwitches.MSTest/FeatureSwitches.MSTest.csproj b/src/FeatureSwitches.MSTest/FeatureSwitches.MSTest.csproj index cc28926..0031aa8 100644 --- a/src/FeatureSwitches.MSTest/FeatureSwitches.MSTest.csproj +++ b/src/FeatureSwitches.MSTest/FeatureSwitches.MSTest.csproj @@ -11,6 +11,6 @@ - + diff --git a/src/FeatureSwitches.MSTest/FeatureTestMethodAttribute.cs b/src/FeatureSwitches.MSTest/FeatureTestMethodAttribute.cs index 38b5ef7..f5a157d 100644 --- a/src/FeatureSwitches.MSTest/FeatureTestMethodAttribute.cs +++ b/src/FeatureSwitches.MSTest/FeatureTestMethodAttribute.cs @@ -1,6 +1,6 @@ using System.Collections.Concurrent; +using System.Runtime.CompilerServices; using FeatureSwitches.Definitions; -using Microsoft.VisualStudio.TestTools.UnitTesting; [assembly: CLSCompliant(true)] @@ -9,9 +9,6 @@ namespace FeatureSwitches.MSTest; [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public sealed class FeatureTestMethodAttribute : TestMethodAttribute { - private static readonly char[] ArgumentSeparator = [',']; - private static readonly ConcurrentDictionary> ExecutingTestFeatures = new(); - /// /// Initializes a new instance of the class. /// @@ -19,12 +16,25 @@ public sealed class FeatureTestMethodAttribute : TestMethodAttribute /// A comma separated string of features that are always on. /// A comma separated string of features that are always off. /// The display name. - public FeatureTestMethodAttribute(string? onOff, string? on = null, string? off = null, string? displayName = null) - : base(displayName) + public FeatureTestMethodAttribute( + string? onOff, + string? on = null, + string? off = null, + string? displayName = null, +#pragma warning disable CA1019 // Define accessors for attribute arguments +#pragma warning disable CS1573 // Parameter has no matching param tag in the XML comment (but other parameters do) +#pragma warning disable SA1611 // Element parameters should be documented + [CallerFilePath] string callerFilePath = "", + [CallerLineNumber] int callerLineNumber = -1) +#pragma warning restore SA1611 // Element parameters should be documented +#pragma warning restore CS1573 // Parameter has no matching param tag in the XML comment (but other parameters do) +#pragma warning restore CA1019 // Define accessors for attribute arguments + : base(callerFilePath, callerLineNumber) { this.OnOff = onOff; this.On = on; this.Off = off; + this.DisplayName = displayName; } /// @@ -47,7 +57,7 @@ public static IReadOnlyList GetFeatures(TestContext context) { ArgumentNullException.ThrowIfNull(context); - if (ExecutingTestFeatures.TryGetValue(context.FullyQualifiedTestClassName + '/' + context.TestName, out var features)) + if (FeatureTestMethodAttributeHelpers.ExecutingTestFeatures.TryGetValue(context.FullyQualifiedTestClassName + '/' + context.TestName, out var features)) { return features; } @@ -55,13 +65,13 @@ public static IReadOnlyList GetFeatures(TestContext context) return []; } - public override TestResult[] Execute(ITestMethod testMethod) + public override async Task ExecuteAsync(ITestMethod testMethod) { ArgumentNullException.ThrowIfNull(testMethod); static string[] Convert(string? arg) { - return arg?.Split(ArgumentSeparator, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToArray() ?? []; + return arg?.Split(FeatureTestMethodAttributeHelpers.ArgumentSeparator, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToArray() ?? []; } var on = Convert(this.On); @@ -69,7 +79,7 @@ static string[] Convert(string? arg) var onOff = Convert(this.OnOff); var results = new List(); - var featuresTestValues = testMethod.GetAttributes(false); + var featuresTestValues = testMethod.GetAttributes(); var allFeatures = on.Concat(off).Concat(onOff); var onCombinations = Enumerable.Range(0, 1 << onOff.Length) @@ -124,9 +134,9 @@ static string[] Convert(string? arg) var fullMethodName = testMethod.TestClassName + '/' + testMethod.TestMethodName; var sortedFeatures = featureDefinitions.OrderBy(x => x.Name).ToList(); - ExecutingTestFeatures.TryAdd(fullMethodName, sortedFeatures); + FeatureTestMethodAttributeHelpers.ExecutingTestFeatures.TryAdd(fullMethodName, sortedFeatures); - var result = testMethod.Invoke(null); + var result = await testMethod.InvokeAsync(null).ConfigureAwait(false); static string GetOnOffValue(object? value) { @@ -142,10 +152,20 @@ static string GetOnOffValue(object? value) results.Add(result); - ExecutingTestFeatures.TryRemove(fullMethodName, out _); + FeatureTestMethodAttributeHelpers.ExecutingTestFeatures.TryRemove(fullMethodName, out _); } } return [.. results]; } + + // This internal static class is a workaround for the diagnostic warning: + // " MSTEST0057: TestMethodAttribute derived class 'FeatureTestMethodAttribute' should add CallerFilePath and CallerLineNumber parameters to its constructor (https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0057 " + // The diagnostic does not recognize that when adding static fields a static constructor is generated by the compiler, + // and that static constructor can not have the CallerFilePath and CallerLineNumber parameters. + internal static class FeatureTestMethodAttributeHelpers + { + internal static readonly char[] ArgumentSeparator = [',']; + internal static readonly ConcurrentDictionary> ExecutingTestFeatures = new(); + } } diff --git a/src/FeatureSwitches.ServiceCollection/FeatureSwitches.ServiceCollection.csproj b/src/FeatureSwitches.ServiceCollection/FeatureSwitches.ServiceCollection.csproj index 88b9c02..71d2fbd 100644 --- a/src/FeatureSwitches.ServiceCollection/FeatureSwitches.ServiceCollection.csproj +++ b/src/FeatureSwitches.ServiceCollection/FeatureSwitches.ServiceCollection.csproj @@ -3,11 +3,11 @@ ServiceCollection extensions for FeatureSwitches FeatureSwitches.ServiceCollection - 9.0.0 + 10.0.0 - + diff --git a/src/FeatureSwitches/IFeatureService.cs b/src/FeatureSwitches/IFeatureService.cs index 611463e..fdabc4d 100644 --- a/src/FeatureSwitches/IFeatureService.cs +++ b/src/FeatureSwitches/IFeatureService.cs @@ -1,4 +1,4 @@ -namespace FeatureSwitches; +namespace FeatureSwitches; public interface IFeatureService { @@ -57,7 +57,7 @@ public interface IFeatureService /// The featureswitch name. /// A cancellation token. /// The current switch value, or null if the feature doesn't exist. - public Task GetBytes(string feature, CancellationToken cancellationToken = default); + Task GetBytes(string feature, CancellationToken cancellationToken = default); /// /// Gets the current value of the featureswitch within the specified evaluation context as a byte array. @@ -68,5 +68,5 @@ public interface IFeatureService /// A cancellation token. /// The evaluation context type. /// The current switch value, or null if the feature doesn't exist. - public Task GetBytes(string feature, TEvaluationContext evaluationContext, CancellationToken cancellationToken = default); + Task GetBytes(string feature, TEvaluationContext evaluationContext, CancellationToken cancellationToken = default); } diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 120c6cd..00ec539 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -2,7 +2,7 @@ - net9.0 + net10.0 false diff --git a/test/FeatureSwitches.Test/FeatureSwitches.Test.csproj b/test/FeatureSwitches.Test/FeatureSwitches.Test.csproj index 6f7124e..c56f2ac 100644 --- a/test/FeatureSwitches.Test/FeatureSwitches.Test.csproj +++ b/test/FeatureSwitches.Test/FeatureSwitches.Test.csproj @@ -1,6 +1,6 @@ - + diff --git a/test/FeatureSwitches.Test/IntegrationTest/FeatureServiceIntegrationTest.cs b/test/FeatureSwitches.Test/IntegrationTest/FeatureServiceIntegrationTest.cs index ee082e2..7d7363b 100644 --- a/test/FeatureSwitches.Test/IntegrationTest/FeatureServiceIntegrationTest.cs +++ b/test/FeatureSwitches.Test/IntegrationTest/FeatureServiceIntegrationTest.cs @@ -104,7 +104,7 @@ public async Task Invalid_request() var featureDatabase = this.sp.GetRequiredService(); featureDatabase.SetFeature("Egg"); var featureService = this.sp.GetRequiredService(); - await Assert.ThrowsExceptionAsync(() => featureService.GetValue("Egg")); + await Assert.ThrowsAsync(() => featureService.GetValue("Egg")); Assert.IsTrue(await featureService.IsOn("Egg")); } @@ -124,7 +124,7 @@ public async Task IsOn_with_non_boolean_feature() featureDatabase.SetFeature("Switch", isOn: true, offValue: "Off", onValue: "On"); var featureService = this.sp.GetRequiredService(); - await Assert.ThrowsExceptionAsync(() => featureService.IsOn("Switch")); + await Assert.ThrowsAsync(() => featureService.IsOn("Switch")); } [TestMethod] @@ -474,7 +474,7 @@ public async Task All_features() var featureService = this.sp.GetRequiredService(); var features = await featureService.GetFeatures(); - Assert.AreEqual(2, features.Length); + Assert.HasCount(2, features); Assert.AreEqual("FeatureA", features[0]); Assert.AreEqual("FeatureB", features[1]); } diff --git a/test/FeatureSwitches.Test/MSTest/FeatureTestMethodAttributeTest.cs b/test/FeatureSwitches.Test/MSTest/FeatureTestMethodAttributeTest.cs index e626cd3..df1c4e7 100644 --- a/test/FeatureSwitches.Test/MSTest/FeatureTestMethodAttributeTest.cs +++ b/test/FeatureSwitches.Test/MSTest/FeatureTestMethodAttributeTest.cs @@ -10,71 +10,71 @@ public sealed class FeatureTestMethodAttributeTest public TestContext TestContext { get; set; } = null!; [TestMethod] - public void Single_feature_on_off() + public async Task Single_feature_on_off() { var attr = new FeatureTestMethodAttribute(onOff: "A"); var testMethod = new MockTestMethod(this.TestContext); - var testResults = attr.Execute(testMethod); + var testResults = await attr.ExecuteAsync(testMethod); - Assert.AreEqual(2, testMethod.Executions.Count); + Assert.HasCount(2, testMethod.Executions); - Assert.AreEqual(1, testMethod.Executions[0].Features.Count); + Assert.HasCount(1, testMethod.Executions[0].Features); Assert.AreEqual("A", testMethod.Executions[0].Features[0].Name); - Assert.AreEqual(false, testMethod.Executions[0].Features[0].IsOn); - Assert.AreEqual(true, testMethod.Executions[0].Features[0].OnValue); - Assert.AreEqual(false, testMethod.Executions[0].Features[0].OffValue); + Assert.IsFalse(testMethod.Executions[0].Features[0].IsOn); + Assert.IsTrue((bool?)testMethod.Executions[0].Features[0].OnValue); + Assert.IsFalse((bool?)testMethod.Executions[0].Features[0].OffValue); - Assert.AreEqual(1, testMethod.Executions[1].Features.Count); + Assert.HasCount(1, testMethod.Executions[1].Features); Assert.AreEqual("A", testMethod.Executions[1].Features[0].Name); - Assert.AreEqual(true, testMethod.Executions[1].Features[0].IsOn); - Assert.AreEqual(true, testMethod.Executions[1].Features[0].OnValue); - Assert.AreEqual(false, testMethod.Executions[1].Features[0].OffValue); + Assert.IsTrue(testMethod.Executions[1].Features[0].IsOn); + Assert.IsTrue((bool?)testMethod.Executions[1].Features[0].OnValue); + Assert.IsFalse((bool?)testMethod.Executions[1].Features[0].OffValue); - Assert.AreEqual(2, testResults.Length); + Assert.HasCount(2, testResults); Assert.AreEqual($"{nameof(this.Single_feature_on_off)} (A: off)", testResults[0].DisplayName); Assert.AreEqual($"{nameof(this.Single_feature_on_off)} (A: on)", testResults[1].DisplayName); } [TestMethod] - public void Two_features_on_off() + public async Task Two_features_on_off() { var attr = new FeatureTestMethodAttribute(onOff: "A,B"); var testMethod = new MockTestMethod(this.TestContext); - var testResults = attr.Execute(testMethod); + var testResults = await attr.ExecuteAsync(testMethod); - Assert.AreEqual(4, testMethod.Executions.Count); + Assert.HasCount(4, testMethod.Executions); - Assert.AreEqual(2, testMethod.Executions[0].Features.Count); + Assert.HasCount(2, testMethod.Executions[0].Features); Assert.AreEqual("A", testMethod.Executions[0].Features[0].Name); - Assert.AreEqual(false, testMethod.Executions[0].Features[0].IsOn); - Assert.AreEqual(true, testMethod.Executions[0].Features[0].OnValue); - Assert.AreEqual(false, testMethod.Executions[0].Features[0].OffValue); + Assert.IsFalse(testMethod.Executions[0].Features[0].IsOn); + Assert.IsTrue((bool?)testMethod.Executions[0].Features[0].OnValue); + Assert.IsFalse((bool?)testMethod.Executions[0].Features[0].OffValue); Assert.AreEqual("B", testMethod.Executions[0].Features[1].Name); - Assert.AreEqual(false, testMethod.Executions[0].Features[1].IsOn); - Assert.AreEqual(true, testMethod.Executions[0].Features[1].OnValue); - Assert.AreEqual(false, testMethod.Executions[0].Features[1].OffValue); + Assert.IsFalse(testMethod.Executions[0].Features[1].IsOn); + Assert.IsTrue((bool?)testMethod.Executions[0].Features[1].OnValue); + Assert.IsFalse((bool?)testMethod.Executions[0].Features[1].OffValue); - Assert.AreEqual(2, testMethod.Executions[1].Features.Count); + Assert.HasCount(2, testMethod.Executions[1].Features); Assert.AreEqual("A", testMethod.Executions[1].Features[0].Name); - Assert.AreEqual(true, testMethod.Executions[1].Features[0].IsOn); + Assert.IsTrue(testMethod.Executions[1].Features[0].IsOn); Assert.AreEqual("B", testMethod.Executions[1].Features[1].Name); - Assert.AreEqual(false, testMethod.Executions[1].Features[1].IsOn); + Assert.IsFalse(testMethod.Executions[1].Features[1].IsOn); - Assert.AreEqual(2, testMethod.Executions[2].Features.Count); + Assert.HasCount(2, testMethod.Executions[2].Features); Assert.AreEqual("A", testMethod.Executions[2].Features[0].Name); - Assert.AreEqual(false, testMethod.Executions[2].Features[0].IsOn); + Assert.IsFalse(testMethod.Executions[2].Features[0].IsOn); Assert.AreEqual("B", testMethod.Executions[2].Features[1].Name); - Assert.AreEqual(true, testMethod.Executions[2].Features[1].IsOn); + Assert.IsTrue(testMethod.Executions[2].Features[1].IsOn); - Assert.AreEqual(2, testMethod.Executions[3].Features.Count); + Assert.HasCount(2, testMethod.Executions[3].Features); Assert.AreEqual("A", testMethod.Executions[3].Features[0].Name); - Assert.AreEqual(true, testMethod.Executions[3].Features[0].IsOn); + Assert.IsTrue(testMethod.Executions[3].Features[0].IsOn); Assert.AreEqual("B", testMethod.Executions[3].Features[1].Name); - Assert.AreEqual(true, testMethod.Executions[3].Features[1].IsOn); + Assert.IsTrue(testMethod.Executions[3].Features[1].IsOn); - Assert.AreEqual(4, testResults.Length); + Assert.HasCount(4, testResults); Assert.AreEqual($"{nameof(this.Two_features_on_off)} (A: off, B: off)", testResults[0].DisplayName); Assert.AreEqual($"{nameof(this.Two_features_on_off)} (A: on, B: off)", testResults[1].DisplayName); Assert.AreEqual($"{nameof(this.Two_features_on_off)} (A: off, B: on)", testResults[2].DisplayName); @@ -82,83 +82,83 @@ public void Two_features_on_off() } [TestMethod] - public void Single_feature_on_off_and_feature_on_and_feature_off() + public async Task Single_feature_on_off_and_feature_on_and_feature_off() { var attr = new FeatureTestMethodAttribute(onOff: "A", on: "B", off: "C"); var testMethod = new MockTestMethod(this.TestContext); - var testResults = attr.Execute(testMethod); + var testResults = await attr.ExecuteAsync(testMethod); - Assert.AreEqual(2, testMethod.Executions.Count); + Assert.HasCount(2, testMethod.Executions); - Assert.AreEqual(3, testMethod.Executions[0].Features.Count); + Assert.HasCount(3, testMethod.Executions[0].Features); Assert.AreEqual("A", testMethod.Executions[0].Features[0].Name); - Assert.AreEqual(false, testMethod.Executions[0].Features[0].IsOn); + Assert.IsFalse(testMethod.Executions[0].Features[0].IsOn); Assert.AreEqual("B", testMethod.Executions[0].Features[1].Name); - Assert.AreEqual(true, testMethod.Executions[0].Features[1].IsOn); + Assert.IsTrue(testMethod.Executions[0].Features[1].IsOn); Assert.AreEqual("C", testMethod.Executions[0].Features[2].Name); - Assert.AreEqual(false, testMethod.Executions[0].Features[2].IsOn); + Assert.IsFalse(testMethod.Executions[0].Features[2].IsOn); - Assert.AreEqual(3, testMethod.Executions[1].Features.Count); + Assert.HasCount(3, testMethod.Executions[1].Features); Assert.AreEqual("A", testMethod.Executions[1].Features[0].Name); - Assert.AreEqual(true, testMethod.Executions[1].Features[0].IsOn); + Assert.IsTrue(testMethod.Executions[1].Features[0].IsOn); Assert.AreEqual("B", testMethod.Executions[1].Features[1].Name); - Assert.AreEqual(true, testMethod.Executions[1].Features[1].IsOn); + Assert.IsTrue(testMethod.Executions[1].Features[1].IsOn); Assert.AreEqual("C", testMethod.Executions[1].Features[2].Name); - Assert.AreEqual(false, testMethod.Executions[1].Features[2].IsOn); + Assert.IsFalse(testMethod.Executions[1].Features[2].IsOn); - Assert.AreEqual(2, testResults.Length); + Assert.HasCount(2, testResults); Assert.AreEqual($"{nameof(this.Single_feature_on_off_and_feature_on_and_feature_off)} (A: off, B: on, C: off)", testResults[0].DisplayName); Assert.AreEqual($"{nameof(this.Single_feature_on_off_and_feature_on_and_feature_off)} (A: on, B: on, C: off)", testResults[1].DisplayName); } [TestMethod] - public void Single_feature_on_off_typed() + public async Task Single_feature_on_off_typed() { var attr = new FeatureTestMethodAttribute(onOff: "A"); var testData = new FeatureTestValueAttribute("A", onValue: "High", offValue: "Low"); var testMethod = new MockTestMethod(this.TestContext, [testData]); - var testResults = attr.Execute(testMethod); + var testResults = await attr.ExecuteAsync(testMethod); - Assert.AreEqual(2, testMethod.Executions.Count); + Assert.HasCount(2, testMethod.Executions); Assert.AreEqual("A", testMethod.Executions[0].Features[0].Name); - Assert.AreEqual(false, testMethod.Executions[0].Features[0].IsOn); + Assert.IsFalse(testMethod.Executions[0].Features[0].IsOn); Assert.AreEqual("High", testMethod.Executions[0].Features[0].OnValue); Assert.AreEqual("Low", testMethod.Executions[0].Features[0].OffValue); - Assert.AreEqual(2, testResults.Length); + Assert.HasCount(2, testResults); Assert.AreEqual($"{nameof(this.Single_feature_on_off_typed)} (A: Low)", testResults[0].DisplayName); Assert.AreEqual($"{nameof(this.Single_feature_on_off_typed)} (A: High)", testResults[1].DisplayName); } [TestMethod] - public void Single_feature_on_off_typed_multiple_onvalues() + public async Task Single_feature_on_off_typed_multiple_onvalues() { var attr = new FeatureTestMethodAttribute(onOff: "A"); var testData = new FeatureTestValueAttribute("A", onValues: ["On1", "On2"], offValue: "Off"); var testMethod = new MockTestMethod(this.TestContext, [testData]); - var testResults = attr.Execute(testMethod); + var testResults = await attr.ExecuteAsync(testMethod); - Assert.AreEqual(3, testMethod.Executions.Count); + Assert.HasCount(3, testMethod.Executions); - Assert.AreEqual(false, testMethod.Executions[0].Features[0].IsOn); + Assert.IsFalse(testMethod.Executions[0].Features[0].IsOn); Assert.AreEqual("On1", testMethod.Executions[0].Features[0].OnValue); Assert.AreEqual("Off", testMethod.Executions[0].Features[0].OffValue); - Assert.AreEqual(true, testMethod.Executions[1].Features[0].IsOn); + Assert.IsTrue(testMethod.Executions[1].Features[0].IsOn); Assert.AreEqual("On1", testMethod.Executions[1].Features[0].OnValue); Assert.AreEqual("Off", testMethod.Executions[1].Features[0].OffValue); - Assert.AreEqual(true, testMethod.Executions[2].Features[0].IsOn); + Assert.IsTrue(testMethod.Executions[2].Features[0].IsOn); Assert.AreEqual("On2", testMethod.Executions[2].Features[0].OnValue); Assert.AreEqual("Off", testMethod.Executions[2].Features[0].OffValue); - Assert.AreEqual(3, testResults.Length); + Assert.HasCount(3, testResults); Assert.AreEqual($"{nameof(this.Single_feature_on_off_typed_multiple_onvalues)} (A: Off)", testResults[0].DisplayName); Assert.AreEqual($"{nameof(this.Single_feature_on_off_typed_multiple_onvalues)} (A: On1)", testResults[1].DisplayName); Assert.AreEqual($"{nameof(this.Single_feature_on_off_typed_multiple_onvalues)} (A: On2)", testResults[2].DisplayName); @@ -194,22 +194,22 @@ public MockTestMethod(TestContext testContext, IList testAttributes) public MethodInfo MethodInfo => throw new InvalidOperationException(); - public Attribute[] GetAllAttributes(bool inherit) + public Task InvokeAsync(object[]? arguments) { - throw new InvalidOperationException(); + this.Executions.Add(new() { Features = FeatureTestMethodAttribute.GetFeatures(this.testContext) }); + + return Task.FromResult(new TestResult()); } - public TAttributeType[] GetAttributes(bool inherit) - where TAttributeType : Attribute + public Attribute[]? GetAllAttributes() { - return this.testAttributes.OfType().ToArray(); + return [.. this.testAttributes]; } - public TestResult Invoke(object[]? arguments) + public TAttributeType[] GetAttributes() + where TAttributeType : Attribute { - this.Executions.Add(new() { Features = FeatureTestMethodAttribute.GetFeatures(this.testContext) }); - - return new TestResult(); + return [.. this.testAttributes.OfType()]; } public sealed class MockTestMethodExecution From 9a89316c7e00d8d22990d8a651ab3acf32c889bc Mon Sep 17 00:00:00 2001 From: Jurriaan Wijnberg Date: Wed, 12 Nov 2025 09:20:16 +0100 Subject: [PATCH 2/3] minor --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 21b3bf1..2f9e6b0 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.100", + "version": "10.0.100", "rollForward": "latestMajor" }, "msbuild-sdks": { From 89bafc93b760f8ff3e91e237a9fb1d4b60f0b872 Mon Sep 17 00:00:00 2001 From: Jurriaan Wijnberg Date: Wed, 12 Nov 2025 09:33:32 +0100 Subject: [PATCH 3/3] minor --- global.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/global.json b/global.json index 2f9e6b0..364d60e 100644 --- a/global.json +++ b/global.json @@ -5,5 +5,8 @@ }, "msbuild-sdks": { "MSTest.Sdk": "4.0.2" + }, + "test": { + "runner": "Microsoft.Testing.Platform" } }