更改NUnit测试的名称

时间:2015-06-12 09:28:05

标签: c# .net unit-testing nunit

我希望我的基于NUnit框架的单元测试在Visual Studio测试资源管理器中更具人性化。

例如,我最好不要使用Test_Case_1TestCase1,而是使用Test Case #1, Category: First, Category: Second之类的内容(通过分配来自[Category]属性的值),空格和字符不是在方法名称中允许。

我知道它在xUnit中可能是开箱即用的,但我无法参与其中,因为我使用的是我无法使用xUnit框架实现的自定义。 / p>

是否可以使用NUnit重写单元测试显示名称?到目前为止,我可以看到,FullName的{​​{1}}字段有私人制定者。

是否有其他方式或方法更改TestDetail测试的显示名称?

4 个答案:

答案 0 :(得分:4)

如果您使用参数化测试,则支持此功能,您可以在添加TestName属性时指定TestCase

如果您没有使用TestCase,那么您可以将其用作不太理想的工作来实现您想要做的事情。所以你会像这样声明你的测试:

[TestCase(null,TestName="Test Case #1, Category: First, Category: Second")]
public void TestCase(object ignored)

这不是理想的,因为它不是程序化的,因此您必须手动键入测试名称,而不是从测试方法的属性生成它。您还必须将参数传递给方法,这是ignorednull的内容。当然,您可以开始使用参数化测试,在这种情况下,您将实际值传递给测试。

[TestCase(5,TestName="Test Case #1, Category: First, Category: Second")]
public void TestCase(int someInput) {
    Assert.AreEqual(5, someInput);
}

答案 1 :(得分:3)

您可以创建自己的名称属性:

// 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;
}

答案 2 :(得分:0)

我想以编程方式动态更改参数化 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;
    }
}

答案 3 :(得分:0)

一种可能的方法是创建您自己的 TestAttribute 类并将 Name 实例的属性 NUnit.Framework.Internal.TestMethod 设置为您想要显示的任何文本(参见方法 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”:

Visual Studio Test Explorer

然而,ReSharper 的测试运行程序/资源管理器仍然使用该方法的名称:

ReSharper test Explorer