Skip to content
11 changes: 10 additions & 1 deletion src/Adapter/MSTest.CoreAdapter/Discovery/TypeEnumerator.cs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -204,6 +204,15 @@ internal UnitTestElement GetTestFromMethod(MethodInfo method, bool isDeclaredInT
this.type,
warnings);

// get DisplayName from TestMethodAttribute
var myAttribute = method.GetCustomAttributes(false)
.OfType<TestMethodAttribute>()
.FirstOrDefault();

var testMethodAttribute = myAttribute as TestMethodAttribute;

testElement.DisplayName = testMethodAttribute?.DisplayName ?? method.Name;

return testElement;
}
}
Expand Down
15 changes: 9 additions & 6 deletions src/Adapter/MSTest.CoreAdapter/Extensions/TestCaseExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,22 @@ 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)
{
testMethod.DeclaringClassFullName = declaringClassName;
}

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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ public UnitTestElement(TestMethod testMethod)
/// </summary>
public KeyValuePair<string, string>[] DeploymentItems { get; set; }

/// <summary>
/// Gets or sets the DisplayName
/// </summary>
public string DisplayName { get; set; }

/// <summary>
/// Gets or sets the compiler generated type name for async test method.
/// </summary>
Expand Down Expand Up @@ -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);

Expand Down
24 changes: 24 additions & 0 deletions src/TestFramework/MSTest.Core/Attributes/VSTestAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,30 @@ public virtual TestMethodAttribute GetTestMethodAttribute(TestMethodAttribute te
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class TestMethodAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="TestMethodAttribute"/> class.
/// </summary>
public TestMethodAttribute()
: this(null)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="TestMethodAttribute"/> class.
/// </summary>
/// <param name="displayName">
/// Message specifies reason for ignoring.
/// </param>
public TestMethodAttribute(string displayName)
{
this.DisplayName = displayName;
}

/// <summary>
/// Gets display Name for the Test Window
/// </summary>
public string DisplayName { get; private set; }

/// <summary>
/// Executes a test method.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -529,6 +530,32 @@ 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(DisplayNameTestClass), "DummyAssemblyName");
var methodInfo = typeof(DisplayNameTestClass).GetMethod(nameof(DisplayNameTestClass.TestMethodWithoutDisplayName));

var testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, this.warnings);

Assert.IsNotNull(testElement);
Assert.AreEqual("TestMethodWithoutDisplayName", testElement.DisplayName);
}

[TestMethod]
public void GetTestFromMethodShouldSetDisplayNameFromAttribute()
{
this.SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true);
TypeEnumerator typeEnumerator = this.GetTypeEnumeratorInstance(typeof(DisplayNameTestClass), "DummyAssemblyName");
var methodInfo = typeof(DisplayNameTestClass).GetMethod(nameof(DisplayNameTestClass.TestMethodWithDisplayName));

var testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, this.warnings);

Assert.IsNotNull(testElement);
Assert.AreEqual("Test method display name.", testElement.DisplayName);
}

#endregion

#region private methods
Expand Down Expand Up @@ -601,5 +628,18 @@ public class DummySecondHidingTestClass : DummyOverridingTestClass
}
}

internal abstract class DisplayNameTestClass
{
[TestMethodV2]
public void TestMethodWithoutDisplayName()
{
}

[TestMethodV2("Test method display name.")]
public void TestMethodWithDisplayName()
{
}
}

#endregion
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down