更改 NUnit 测试的名称
Change name of NUnit test
我希望基于 NUnit
框架的单元测试在 Visual Studio 测试资源管理器中命名为更易于阅读。
例如,与其使用 Test_Case_1
或 TestCase1
,不如使用 Test Case #1, Category: First, Category: Second
之类的东西(通过从 [Category]
属性分配值),使用空格和方法名称中不允许使用的字符。
我知道它在 xUnit
中是开箱即用的,但我不能涉及它,因为我正在使用我无法使用 xUnit
框架实现的自定义.
是否可以用 NUnit
重写单元测试显示名称?到目前为止,我可以看到,TestDetail
的 FullName
字段具有私有 setter.
是否有任何其他方式或方法更改 NUnit
测试的显示名称?
如果您使用参数化测试,则支持此功能,您可以在添加 TestCase
属性时指定 TestName
。
如果您不使用 TestCase,那么您可以将其用作不太理想的变通方法来实现您想要做的事情。所以你会像这样声明你的测试:
[TestCase(null,TestName="Test Case #1, Category: First, Category: Second")]
public void TestCase(object ignored)
这并不理想,因为它不是程序化的,所以您必须手动键入测试名称,而不是从测试方法的属性中生成它。您还必须向该方法传递一个参数,这就是 ignored
和 null
的意义所在。当然,您可以开始使用参数化测试,在这种情况下,您将向测试传递实际值。
[TestCase(5,TestName="Test Case #1, Category: First, Category: Second")]
public void TestCase(int someInput) {
Assert.AreEqual(5, someInput);
}
您可以创建自己的名称属性:
// I used the same namespace for convenience
namespace NUnit.Framework
{
public class NameAttribute : NUnitAttribute, IApplyToTest
{
public NameAttribute(string name)
{
Name = name;
}
public string Name { get; set; }
public void ApplyToTest(Test test)
{
test.Properties.Add("Name", Name);
}
}
}
然后您可以像使用常规 NUnit 一样使用它 属性(就像类别和描述一样)。
[Test, Name("My Awesome Test"), Category("Cool.Tests"), Description("All cool tests")]
public void Test313()
{
// Do something
}
您可以在您的 TestContext 中看到数据:
if (TestContext.CurrentContext.Test.Properties.ContainsKey("Name"))
{
name = TestContext.CurrentContext.Test.Properties.Get("Name") as string;
}
我想以编程方式动态更改参数化 NUnit 测试的测试名称,即基于测试数据文件夹中的输入文件。
进行了几次调整,但效果很好:
[Test, TestCaseSource(nameof(GetSites))]
public void TestForEveryFile(object ignored, FileInfo testFile) {
// actual test using 'testFile'
}
public static IEnumerable<TestCaseData> GetSites()
{
foreach (string testFile in Directory.EnumerateFiles("TestData"))
{
FileInfo fileInfo = new FileInfo(testFile);
// Pass '' as first argument to TestCaseData to suppress the default test name
// (seems to be necessary even if TestName is set)
var testCase = new TestCaseData("", fileInfo)
{
// Use the short file name as test name.
// As dots (.) would indicate a test hierarchy, we replace them with '-'.
TestName = fileInfo.Name.Replace(".", "-")
};
yield return testCase;
}
}
一种可能的方法是创建您自己的 TestAttribute
class 并将 NUnit.Framework.Internal.TestMethod
实例的 属性 Name
设置为您想要的任何文本显示(参见下面代码中的方法 TestAttribute.BuildFrom
)。
请注意,此代码只不过是一个 hack。可能会引起我没有想到的副作用。
using System;
using NUnit.Framework;
using NUnit.Framework.Interfaces;
using NUnit.Framework.Internal;
using NUnit.Framework.Internal.Builders;
namespace NUnitTesting.Tests
{
[TestFixture(TestName = "Display name of Tests")]
public class Tests
{
[Test(DisplayName = "Display name of Test1")]
public void Test1()
{
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class TestAttribute : NUnitAttribute, ISimpleTestBuilder, IApplyToTest, IImplyFixture
{
/// <summary>
/// The author of this test.
/// </summary>
public String Author { get; set; }
/// <summary>
/// Descriptive text for this test.
/// </summary>
public String Description { get; set; }
/// <summary>
/// The display name for this test.
/// </summary>
public String DisplayName { get; set; }
/// <summary>
/// Gets or sets the expected result.
/// Not valid if the test method has parameters.
/// </summary>
/// <value>The result.</value>
public Object ExpectedResult
{
get => this.expectedResult;
set
{
this.expectedResult = value;
this.hasExpectedResult = true;
}
}
/// <summary>
/// The type that this test is testing.
/// </summary>
public Type TestOf { get; set; }
#region IApplyToTest Members
/// <summary>
/// Modifies a test by adding a description, if not already set.
/// </summary>
/// <param name="test">The test to modify.</param>
public void ApplyToTest(Test test)
{
if (!(test.Method is Object))
{
throw new ArgumentException("This attribute must only be applied to tests that have an associated method.", nameof(test));
}
if (!test.Properties.ContainsKey(PropertyNames.Description) && this.Description != null)
test.Properties.Set(PropertyNames.Description, this.Description);
if (!test.Properties.ContainsKey(PropertyNames.Author) && this.Author != null)
test.Properties.Set(PropertyNames.Author, this.Author);
if (!test.Properties.ContainsKey(PropertyNames.TestOf) && this.TestOf != null)
test.Properties.Set(PropertyNames.TestOf, this.TestOf.FullName);
if (this.hasExpectedResult && test.Method.GetParameters().Length > 0)
test.MakeInvalid("The 'TestAttribute.ExpectedResult' property may not be used on parameterized methods.");
}
#endregion
#region ISimpleTestBuilder Members
/// <summary>
/// Builds a single test from the specified method and context.
/// </summary>
/// <param name="method">The method for which a test is to be constructed.</param>
/// <param name="suite">The suite to which the test will be added.</param>
public TestMethod BuildFrom(IMethodInfo method, Test suite)
{
TestCaseParameters parms = null;
if (this.hasExpectedResult)
{
parms = new TestCaseParameters();
parms.ExpectedResult = this.ExpectedResult;
}
var testMethod = this.builder.BuildTestMethod(method, suite, parms);
testMethod.Name = this.DisplayName;
return testMethod;
}
#endregion
private readonly NUnitTestCaseBuilder builder = new NUnitTestCaseBuilder();
private Object expectedResult;
private Boolean hasExpectedResult = false; // needed in case result is set to null
}
}
这样,至少 Visual Studio 测试资源管理器显示“Test1 的显示名称”而不仅仅是“Test1”:
但是 ReSharper 的测试 runner/explorer 仍然使用该方法的名称:
除了上面的@forsvarir 回答之外,以下内容现在有效:
[TestCase(TestName = "Test case name")]
Public void TestSomething()
{
...
}
我希望基于 NUnit
框架的单元测试在 Visual Studio 测试资源管理器中命名为更易于阅读。
例如,与其使用 Test_Case_1
或 TestCase1
,不如使用 Test Case #1, Category: First, Category: Second
之类的东西(通过从 [Category]
属性分配值),使用空格和方法名称中不允许使用的字符。
我知道它在 xUnit
中是开箱即用的,但我不能涉及它,因为我正在使用我无法使用 xUnit
框架实现的自定义.
是否可以用 NUnit
重写单元测试显示名称?到目前为止,我可以看到,TestDetail
的 FullName
字段具有私有 setter.
是否有任何其他方式或方法更改 NUnit
测试的显示名称?
如果您使用参数化测试,则支持此功能,您可以在添加 TestCase
属性时指定 TestName
。
如果您不使用 TestCase,那么您可以将其用作不太理想的变通方法来实现您想要做的事情。所以你会像这样声明你的测试:
[TestCase(null,TestName="Test Case #1, Category: First, Category: Second")]
public void TestCase(object ignored)
这并不理想,因为它不是程序化的,所以您必须手动键入测试名称,而不是从测试方法的属性中生成它。您还必须向该方法传递一个参数,这就是 ignored
和 null
的意义所在。当然,您可以开始使用参数化测试,在这种情况下,您将向测试传递实际值。
[TestCase(5,TestName="Test Case #1, Category: First, Category: Second")]
public void TestCase(int someInput) {
Assert.AreEqual(5, someInput);
}
您可以创建自己的名称属性:
// I used the same namespace for convenience
namespace NUnit.Framework
{
public class NameAttribute : NUnitAttribute, IApplyToTest
{
public NameAttribute(string name)
{
Name = name;
}
public string Name { get; set; }
public void ApplyToTest(Test test)
{
test.Properties.Add("Name", Name);
}
}
}
然后您可以像使用常规 NUnit 一样使用它 属性(就像类别和描述一样)。
[Test, Name("My Awesome Test"), Category("Cool.Tests"), Description("All cool tests")]
public void Test313()
{
// Do something
}
您可以在您的 TestContext 中看到数据:
if (TestContext.CurrentContext.Test.Properties.ContainsKey("Name"))
{
name = TestContext.CurrentContext.Test.Properties.Get("Name") as string;
}
我想以编程方式动态更改参数化 NUnit 测试的测试名称,即基于测试数据文件夹中的输入文件。
进行了几次调整,但效果很好:
[Test, TestCaseSource(nameof(GetSites))]
public void TestForEveryFile(object ignored, FileInfo testFile) {
// actual test using 'testFile'
}
public static IEnumerable<TestCaseData> GetSites()
{
foreach (string testFile in Directory.EnumerateFiles("TestData"))
{
FileInfo fileInfo = new FileInfo(testFile);
// Pass '' as first argument to TestCaseData to suppress the default test name
// (seems to be necessary even if TestName is set)
var testCase = new TestCaseData("", fileInfo)
{
// Use the short file name as test name.
// As dots (.) would indicate a test hierarchy, we replace them with '-'.
TestName = fileInfo.Name.Replace(".", "-")
};
yield return testCase;
}
}
一种可能的方法是创建您自己的 TestAttribute
class 并将 NUnit.Framework.Internal.TestMethod
实例的 属性 Name
设置为您想要的任何文本显示(参见下面代码中的方法 TestAttribute.BuildFrom
)。
请注意,此代码只不过是一个 hack。可能会引起我没有想到的副作用。
using System;
using NUnit.Framework;
using NUnit.Framework.Interfaces;
using NUnit.Framework.Internal;
using NUnit.Framework.Internal.Builders;
namespace NUnitTesting.Tests
{
[TestFixture(TestName = "Display name of Tests")]
public class Tests
{
[Test(DisplayName = "Display name of Test1")]
public void Test1()
{
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class TestAttribute : NUnitAttribute, ISimpleTestBuilder, IApplyToTest, IImplyFixture
{
/// <summary>
/// The author of this test.
/// </summary>
public String Author { get; set; }
/// <summary>
/// Descriptive text for this test.
/// </summary>
public String Description { get; set; }
/// <summary>
/// The display name for this test.
/// </summary>
public String DisplayName { get; set; }
/// <summary>
/// Gets or sets the expected result.
/// Not valid if the test method has parameters.
/// </summary>
/// <value>The result.</value>
public Object ExpectedResult
{
get => this.expectedResult;
set
{
this.expectedResult = value;
this.hasExpectedResult = true;
}
}
/// <summary>
/// The type that this test is testing.
/// </summary>
public Type TestOf { get; set; }
#region IApplyToTest Members
/// <summary>
/// Modifies a test by adding a description, if not already set.
/// </summary>
/// <param name="test">The test to modify.</param>
public void ApplyToTest(Test test)
{
if (!(test.Method is Object))
{
throw new ArgumentException("This attribute must only be applied to tests that have an associated method.", nameof(test));
}
if (!test.Properties.ContainsKey(PropertyNames.Description) && this.Description != null)
test.Properties.Set(PropertyNames.Description, this.Description);
if (!test.Properties.ContainsKey(PropertyNames.Author) && this.Author != null)
test.Properties.Set(PropertyNames.Author, this.Author);
if (!test.Properties.ContainsKey(PropertyNames.TestOf) && this.TestOf != null)
test.Properties.Set(PropertyNames.TestOf, this.TestOf.FullName);
if (this.hasExpectedResult && test.Method.GetParameters().Length > 0)
test.MakeInvalid("The 'TestAttribute.ExpectedResult' property may not be used on parameterized methods.");
}
#endregion
#region ISimpleTestBuilder Members
/// <summary>
/// Builds a single test from the specified method and context.
/// </summary>
/// <param name="method">The method for which a test is to be constructed.</param>
/// <param name="suite">The suite to which the test will be added.</param>
public TestMethod BuildFrom(IMethodInfo method, Test suite)
{
TestCaseParameters parms = null;
if (this.hasExpectedResult)
{
parms = new TestCaseParameters();
parms.ExpectedResult = this.ExpectedResult;
}
var testMethod = this.builder.BuildTestMethod(method, suite, parms);
testMethod.Name = this.DisplayName;
return testMethod;
}
#endregion
private readonly NUnitTestCaseBuilder builder = new NUnitTestCaseBuilder();
private Object expectedResult;
private Boolean hasExpectedResult = false; // needed in case result is set to null
}
}
这样,至少 Visual Studio 测试资源管理器显示“Test1 的显示名称”而不仅仅是“Test1”:
但是 ReSharper 的测试 runner/explorer 仍然使用该方法的名称:
除了上面的@forsvarir 回答之外,以下内容现在有效:
[TestCase(TestName = "Test case name")]
Public void TestSomething()
{
...
}