What happened to the TestContext.ManagedType property?
Describe the bug
So referencing the online doc reference for the TestContext..ManagedType property and for 3.11.0 it is listed and the option for 4.0.0 is currently not available in the dropdown list. When I review the current code for TextContext.cs, the property is not present as it looks to have been removed in commit 5e79594this merge on June 26th. Realizing that there may be a valid reason for this change, I would note that it is not mentioned in the v3 to v4 migration document.
That said, the reason I ask is we reference this property in REST and Selenium test automation projects but having updated the base framework for these test projects to use MSTest.TestFramework.4.0.0, I am now getting the following compiler error:
'TestContext' does not contain a definition for 'ManagedType' and no accessible extension method 'ManagedType' accepting a first argument of type 'TestContext' could be found (are you missing a using directive or an assembly reference?)
Steps To Reproduce
See attached .NET 9 test project/solution
using System;
using System.Linq;
namespace MSTestManagedTypeIssue;
[TestClass]
public sealed class UnitTests
{
[AssemblyCleanup]
public static void AssemblyCleanup()
{
}
[AssemblyInitialize]
public static void AssemblyInitialize(TestContext context)
{
var testType = GetAutomationTestType(context);
Console.WriteLine(testType);
}
[TestMethod, Description("Assert that the sum of two values equal the specified result")]
[DataRow(1, 2, 3)]
[DataRow(2, 3, 5)]
[DataRow(3, 4, 7)]
[DataRow(4, 5, 9)]
[DataRow(5, 6, 11)]
public void Debug_ShouldBe(int a, int b, int expected)
{
var actual = Math.Abs(a + b);
Assert.AreEqual(expected, actual, $" because the sum of {a} + {b} should be {expected}!");
}
private static AutomationTestTypes GetAutomationTestType(TestContext context)
{
var managedType = context.ManagedType?.Split(".").Last();
return true switch
{
{ } when managedType!.Contains("ApiTests", StringComparison.OrdinalIgnoreCase) => AutomationTestTypes.Api,
{ } when managedType!.Contains("UnitTests", StringComparison.OrdinalIgnoreCase) => AutomationTestTypes.Unit,
_ => AutomationTestTypes.Web,
};
}
}
public enum AutomationTestTypes
{
Api,
Unit,
Web
}
//.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Configurations>Debug;Release</Configurations>
<IsTestProject>true</IsTestProject>
<NoWarn>$(NoWarn);MSTEST0001</NoWarn>
<OutputType>Library</OutputType>
<Platforms>AnyCPU</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AwesomeAssertions" Version="9.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" Version="18.1.0" />
<PackageReference Include="Microsoft.Testing.Extensions.TrxReport" Version="2.0.0" />
<PackageReference Include="MSTest.TestAdapter" Version="4.0.0" />
<PackageReference Include="MSTest.TestFramework" Version="4.0.0" />
</ItemGroup>
<ItemGroup>
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
</ItemGroup>
</Project>
Thanks for reporting this. Can you try using FullyQualifiedTestClassName instead? We indeed need to update the migration guide for this.
Can you try using
FullyQualifiedTestClassNameinstead?
Unfortunately that was the first thing I tried before submitting this issue and it throws a not found exception.
Assembly Initialization method MSTestManagedTypeIssue.UnitTests.AssemblyInitialize threw exception. System.Collections.Generic.KeyNotFoundException: The given key 'FullyQualifiedTestClassName' was not present in the dictionary.. Aborting test execution. at System.Collections.Generic.Dictionary
2.get_Item(TKey key) at Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.GetProperty[T](String name) in /_/src/TestFramework/TestFramework.Extensions/TestContext.cs:line 194 at Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.get_FullyQualifiedTestClassName() in /_/src/TestFramework/TestFramework.Extensions/TestContext.cs:line 112 at MSTestManagedTypeIssue.UnitTests.GetAutomationTestType(TestContext context) in C:\Code\MSTestIssue\UnitTests.cs:line 37 at MSTestManagedTypeIssue.UnitTests.AssemblyInitialize(TestContext context) in C:\Code\MSTestIssue\UnitTests.cs:line 17 at InvokeStub_UnitTests.AssemblyInitialize(Object, Span1) at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
That is expected. You cannot access this from assembly initialize. How would you expect a value to be meaningful during assembly/class initialization?
Think about it in the context of test overhead when initializing the assembly. For Web UI test methods, class initialization creates/destroys an Appium or Selenium driver for each and every test which adds considerable delays. To avoid that, we initialize the driver at the assembly level but there are also API tests which do not require such drivers but things like the TestCategoryAttribute is not yet available when the class is initialized, thus the workaround was to use a naming convention for the calling test classes, which is then evaluated against the AutomationTestTypes enum as expressed in the GetAutomationTestType(TestContext context) method in the code above and the attached solution.
The thing is, in the context of assembly initialize, we are not running a specific test. So it doesn't make sense to provide any class name.
Guess I am inferring/attributing something other than what MS intends when it defines the AssemblyInitializeAttribute!
Identifies a method that contains code to be used before all tests in the assembly have run and to allocate resources obtained by the assembly. This class cannot be inherited.
I don't think this documentation contradicts the current implementation. AssemblyInitialize is run before all tests, but its TestContext cannot tell you which test is being run, because it's not yet running.
Appologies, not trying to be confrontational, but I do find it ironic that for the AssemblyInitializeAttribute reference linked above, the example on the page references the TestName property in the example.
[TestClass()]
public sealed class DivideClassTest
{
[AssemblyInitialize()]
public static void AssemblyInit(TestContext context)
{
MessageBox.Show("AssemblyInit " + context.TestName);
}
Sorry, I only looked at the part of the documentation that quoted. That documentation page dates back to MSTest v1, however, and that example there doesn't really make sense. I think the current behavior makes more sense.
Sounds like I am going to have to do some serious refactoring.