【问题标题】:Change name of NUnit test更改 NUnit 测试的名称
【发布时间】:2015-06-12 09:28:05
【问题描述】:

我希望基于 NUnit 框架的单元测试在 Visual Studio 测试资源管理器中更易于阅读。

例如,与其使用Test_Case_1TestCase1,不如使用Test Case #1, Category: First, Category: Second 之类的东西(也可以从[Category] 属性中分配值),方法名称中不允许使用空格和字符。

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

是否可以用NUnit 重写单元测试显示名称?到目前为止,我可以看到,TestDetailFullName 字段具有私有设置器。

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

【问题讨论】:

    标签: c# .net unit-testing nunit


    【解决方案1】:

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

    如果您不使用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);
    }
    

    【讨论】:

    • 你是对的...这是目前唯一可能的方法...但是,我已经提交了issue,所以将来可能会改变默认行为。
    【解决方案2】:

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

    // 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 属性一样使用它(就像 Category 和 Description)。

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

    【讨论】:

      【解决方案3】:

      除了上面的@forsvarir 答案之外,现在可以使用以下方法:

      [TestCase(TestName = "Test case name")]
      Public void TestSomething()
      {
         ...
      }
      

      【讨论】:

      • 我用nameof(..)来显示测试方法的实际名称,所以[TestCase(TestName = nameof(TargetClass.MethodToTest))]
      • @Rekshino, [TestCase(TestName = nameof(TargetClass.MethodToTest))] 正在打印方法名称,而不是执行方法并使用它的返回值。我在这里错过了什么?
      • @Mounika - “...正在打印方法名称而不是执行方法。”根据the documentation 的说法,它是这样工作的,因为这就是nameof 表达式的作用。更多细节在我对你的问题的回答here
      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        一种可能的方法是创建自己的TestAttribute 类并将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 的测试运行器/探索器仍然使用方法的名称:

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-12-10
          • 2010-10-13
          • 1970-01-01
          • 1970-01-01
          • 2012-06-26
          • 2012-02-20
          相关资源
          最近更新 更多