diff --git a/src/Adapter/MSTest.CoreAdapter/Discovery/TypeEnumerator.cs b/src/Adapter/MSTest.CoreAdapter/Discovery/TypeEnumerator.cs index ea861d1d36..11e6e30f83 100644 --- a/src/Adapter/MSTest.CoreAdapter/Discovery/TypeEnumerator.cs +++ b/src/Adapter/MSTest.CoreAdapter/Discovery/TypeEnumerator.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery @@ -204,6 +204,10 @@ internal UnitTestElement GetTestFromMethod(MethodInfo method, bool isDeclaredInT this.type, warnings); + // get DisplayName from TestMethodAttribute + var testMethodAttribute = this.reflectHelper.GetCustomAttribute(method, typeof(TestMethodAttribute)) as TestMethodAttribute; + testElement.DisplayName = testMethodAttribute?.DisplayName ?? method.Name; + return testElement; } } diff --git a/src/Adapter/MSTest.CoreAdapter/Extensions/TestCaseExtensions.cs b/src/Adapter/MSTest.CoreAdapter/Extensions/TestCaseExtensions.cs index 4b36457406..4f9099c265 100644 --- a/src/Adapter/MSTest.CoreAdapter/Extensions/TestCaseExtensions.cs +++ b/src/Adapter/MSTest.CoreAdapter/Extensions/TestCaseExtensions.cs @@ -24,7 +24,9 @@ internal static UnitTestElement ToUnitTestElement(this TestCase testCase, string var testClassName = testCase.GetPropertyValue(Constants.TestClassNameProperty) as string; var declaringClassName = testCase.GetPropertyValue(Constants.DeclaringClassNameProperty) as string; - TestMethod testMethod = new TestMethod(testCase.DisplayName, testClassName, source, isAsync); + var parts = testCase.FullyQualifiedName.Split('.'); + var name = parts[parts.Length - 1]; + TestMethod testMethod = new TestMethod(name, testClassName, source, isAsync); if (declaringClassName != null && declaringClassName != testClassName) { @@ -32,11 +34,12 @@ internal static UnitTestElement ToUnitTestElement(this TestCase testCase, string } UnitTestElement testElement = new UnitTestElement(testMethod) - { - IsAsync = isAsync, - TestCategory = testCase.GetPropertyValue(Constants.TestCategoryProperty) as string[], - Priority = testCase.GetPropertyValue(Constants.PriorityProperty) as int? - }; + { + IsAsync = isAsync, + TestCategory = testCase.GetPropertyValue(Constants.TestCategoryProperty) as string[], + Priority = testCase.GetPropertyValue(Constants.PriorityProperty) as int?, + DisplayName = testCase.DisplayName + }; return testElement; } diff --git a/src/Adapter/MSTest.CoreAdapter/ObjectModel/UnitTestElement.cs b/src/Adapter/MSTest.CoreAdapter/ObjectModel/UnitTestElement.cs index 744e224101..b39abe7763 100644 --- a/src/Adapter/MSTest.CoreAdapter/ObjectModel/UnitTestElement.cs +++ b/src/Adapter/MSTest.CoreAdapter/ObjectModel/UnitTestElement.cs @@ -72,6 +72,11 @@ public UnitTestElement(TestMethod testMethod) /// public KeyValuePair[] DeploymentItems { get; set; } + /// + /// Gets or sets the DisplayName + /// + public string DisplayName { get; set; } + /// /// Gets or sets the compiler generated type name for async test method. /// @@ -110,7 +115,7 @@ internal TestCase ToTestCase() this.TestMethod.Name); TestCase testCase = new TestCase(fullName, TestAdapter.Constants.ExecutorUri, this.TestMethod.AssemblyName); - testCase.DisplayName = this.TestMethod.Name; + testCase.DisplayName = string.IsNullOrEmpty(this.DisplayName) ? this.TestMethod.Name : this.DisplayName; testCase.SetPropertyValue(TestAdapter.Constants.TestClassNameProperty, this.TestMethod.FullClassName); diff --git a/src/TestFramework/MSTest.Core/Attributes/VSTestAttributes.cs b/src/TestFramework/MSTest.Core/Attributes/VSTestAttributes.cs index dedbe0d6d1..93ecfb13f4 100644 --- a/src/TestFramework/MSTest.Core/Attributes/VSTestAttributes.cs +++ b/src/TestFramework/MSTest.Core/Attributes/VSTestAttributes.cs @@ -67,6 +67,30 @@ public virtual TestMethodAttribute GetTestMethodAttribute(TestMethodAttribute te [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public class TestMethodAttribute : Attribute { + /// + /// Initializes a new instance of the class. + /// + public TestMethodAttribute() + : this(null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Message specifies reason for ignoring. + /// + public TestMethodAttribute(string displayName) + { + this.DisplayName = displayName; + } + + /// + /// Gets display Name for the Test Window + /// + public string DisplayName { get; private set; } + /// /// Executes a test method. /// diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Discovery/TypeEnumeratorTests.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Discovery/TypeEnumeratorTests.cs index d68e2655dc..1ff92165bc 100644 --- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Discovery/TypeEnumeratorTests.cs +++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Discovery/TypeEnumeratorTests.cs @@ -25,6 +25,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Discovery using TestCleanup = FrameworkV1::Microsoft.VisualStudio.TestTools.UnitTesting.TestCleanupAttribute; using TestInitialize = FrameworkV1::Microsoft.VisualStudio.TestTools.UnitTesting.TestInitializeAttribute; using TestMethod = FrameworkV1::Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute; + using TestMethodV2 = FrameworkV2::Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute; using UTF = FrameworkV2::Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -529,6 +530,40 @@ public void GetTestFromMethodShouldSetDeclaringAssemblyName() Assert.AreEqual(otherAssemblyName, testElement.TestMethod.DeclaringAssemblyName); } + [TestMethod] + public void GetTestFromMethodShouldSetDisplayNameToTestMethodNameIfDisplayNameIsNotPresent() + { + this.SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); + TypeEnumerator typeEnumerator = this.GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); + var methodInfo = typeof(DummyTestClass).GetMethod(nameof(DummyTestClass.MethodWithVoidReturnType)); + + // Setup mocks to behave like we have [TestMethod] attribute on the method + this.mockReflectHelper.Setup( + rh => rh.GetCustomAttribute(It.IsAny(), It.IsAny())).Returns(new TestMethodV2()); + + var testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, this.warnings); + + Assert.IsNotNull(testElement); + Assert.AreEqual("MethodWithVoidReturnType", testElement.DisplayName); + } + + [TestMethod] + public void GetTestFromMethodShouldSetDisplayNameFromAttribute() + { + this.SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); + TypeEnumerator typeEnumerator = this.GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); + var methodInfo = typeof(DummyTestClass).GetMethod(nameof(DummyTestClass.MethodWithVoidReturnType)); + + // Setup mocks to behave like we have [TestMethod("Test method display name.")] attribute on the method + this.mockReflectHelper.Setup( + rh => rh.GetCustomAttribute(methodInfo, typeof(TestMethodV2))).Returns(new TestMethodV2("Test method display name.")); + + var testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, this.warnings); + + Assert.IsNotNull(testElement); + Assert.AreEqual("Test method display name.", testElement.DisplayName); + } + #endregion #region private methods diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Extensions/TestCaseExtensionsTests.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Extensions/TestCaseExtensionsTests.cs index 8c6ef2c73f..0b900474be 100644 --- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Extensions/TestCaseExtensionsTests.cs +++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Extensions/TestCaseExtensionsTests.cs @@ -34,7 +34,8 @@ public void ToUnitTestElementShouldReturnUnitTestElementWithFieldsSet() Assert.AreEqual(true, resultUnitTestElement.IsAsync); Assert.AreEqual(2, resultUnitTestElement.Priority); Assert.AreEqual(testCategories, resultUnitTestElement.TestCategory); - Assert.AreEqual("DummyDisplayName", resultUnitTestElement.TestMethod.Name); + Assert.AreEqual("DummyDisplayName", resultUnitTestElement.DisplayName); + Assert.AreEqual("DummyMethod", resultUnitTestElement.TestMethod.Name); Assert.AreEqual("DummyClassName", resultUnitTestElement.TestMethod.FullClassName); Assert.AreEqual(true, resultUnitTestElement.TestMethod.IsAsync); Assert.IsNull(resultUnitTestElement.TestMethod.DeclaringClassFullName); diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/ObjectModel/UnitTestElementTests.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/ObjectModel/UnitTestElementTests.cs index 1143ae8967..4415f56d04 100644 --- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/ObjectModel/UnitTestElementTests.cs +++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/ObjectModel/UnitTestElementTests.cs @@ -76,6 +76,15 @@ public void ToTestCaseShouldSetDisplayName() Assert.AreEqual("M", testCase.DisplayName); } + [TestMethodV1] + public void ToTestCaseShouldSetDisplayNameIfPresent() + { + this.unitTestElement.DisplayName = "Display Name"; + var testCase = this.unitTestElement.ToTestCase(); + + Assert.AreEqual("Display Name", testCase.DisplayName); + } + [TestMethodV1] public void ToTestCaseShouldSetTestClassNameProperty() {