Change name of NUnit test

21.4k views Asked by At

I want my unit-tests based on NUnit framework named a bit more human readable in Visual Studio test explorer.

For example instead of having Test_Case_1 or TestCase1 I would better have something like Test Case #1, Category: First, Category: Second (by assigning values from [Category] attributes as well), with spaces and characters not allowed in methods names.

I know it's out-of-the-box possible in xUnit, but I cannot involve it, since I'm using my customizations that I was not able to implement using xUnit framework.

Is it possible to rewrite unit test display name with NUnit? So far I can see, that FullName field of TestDetail has private setter.

Is any other ways or approaches change display name for NUnit tests?

5

There are 5 answers

1
forsvarir On BEST ANSWER

This is supported if you are using parametrised tests, you can specify the TestName when adding the TestCase attribute.

If you aren't using TestCase, then you could use it as a less than ideal work around to achieve what you're trying to do. So you would declare your test like this:

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

This isn't ideal because it's not programmatic so you have to manually type the test name, rather than generating it from the attributes on the test method. You also have to pass a parameter to the method, which is what the ignored and null is about. Of course, you could start using parametrised tests in which case you'd be passing in an actual value to your tests.

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

Further to @forsvarir answer above, the following now works (from at least 3.6.0, possibly earlier):

[TestCase(TestName = "Test case name")]
public void TestSomething()
{
   ...
}
0
David Liebeherr On

A possible way is to create your own TestAttribute class and set the property Name of the NUnit.Framework.Internal.TestMethod instance to whatever text you want to display (see method TestAttribute.BuildFrom in the code below).

Please be aware that this code is no more than a hack. It might cause side effects that I haven't thought of.

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

This way, at least the Visual Studio Test Explorer shows "Display name of Test1" instead of just "Test1":

Visual Studio Test Explorer

However the test runner/explorer of ReSharper still uses the method's name:

ReSharper test Explorer

0
Daniel Veihelmann On

I wanted to change the test name of a parameterized NUnit test programmatically and dynamically, i.e. based on input files in a test data folder.

It took a couple of adjustments, but this works:

[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;
    }
}
0
Trevy Burgess On

You can create your own Name attribute:

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

You then use it like a regular NUnit property (Just like Category and Description).

[Test, Name("My Awesome Test"), Category("Cool.Tests"), Description("All cool tests")]
public void Test313()
{
    // Do something
}

You can see the data in your TestContext:

if (TestContext.CurrentContext.Test.Properties.ContainsKey("Name"))
{
    name = TestContext.CurrentContext.Test.Properties.Get("Name") as string;
}