ewok
ewok

Reputation: 21513

Nunit: Change test name using TestFixtureSource

I've found a number of question regarding renaming tests in NUnit, but none that mention how to do so while using TestFixtureSource.

I am using the [TestFixtureSource] attribute to configure parameterized tests, like this:

[TestFixtureSource(nameof(GetTestParams))]
public class MyTestClass
{
    private Mock<IMyDependency> _mockDependency;

    private TestData _data;

    private MyClass _objectUnderTest;

    public MyTestClass(TestData data)
    {
        _data = data;
    }

    public static IEnumerable<TestData> GetTestParams()
    {
        yield return new TestData(1, 2, 3);
        yield return new TestData(4, 5, 9);
        yield return new TestData(7, 8, 15);
    }

    [SetUp]
    public void SetUp()
    {
        _mockDependency = new Mock<IMyDependency>();
        _mockDependency.Setup(d => d.GetNum()).Returns(_data.A);

        _objectUnderTest = new MyClass(_mockDependency.Object);
    }

    [Test]
    public void RunTest()
    {
        var result = _objectUnderTest.doSomething(_data.B);
        Assert.That(result, Is.EqualTo(_data.C));
    }
}

public class TestData
{
    public int A { get; set; }
    public int B { get; set; }
    public int C { get; set; }

    public TestData(int a, int b, int c)
    {
        A = a;
        B = b;
        C = c;
    }
}

public class MyClass
{
    private readonly IMyDependency _dependency;

    public MyClass(IMyDependency dependency)
    {
        _dependency = dependency;
    }

    public int doSomething(int b)
    {
        return _dependency.GetNum() + b;
    }
}

public interface IMyDependency
{
    int GetNum();
}

My issue is that all test cases appear to have the same name in the results. They are all simply called "RunTest", which makes it difficult to determine which tests are failing other than simply counting the number of the test and then counting my yield returns to find the correct one.

I would love to be able to set the test name programmatically using an additional property in the TestData class. I attempted to do this using TestContext.CurrentContext.Test.Name = _data.Name, but it turns out this property is readonly so I can't set it.

Is there a way to rename my tests programmatically while using TestFixtureSource?

Upvotes: 1

Views: 1423

Answers (2)

Neil T
Neil T

Reputation: 3285

If I understand correctly, you currently have something like this:

enter image description here

but you would prefer something like this:

enter image description here

This is achievable by overriding the ToString() method of the class whose type is the test fixture's parameter - TestData in your code. For example:

    public class TestData
    {
        <...>
        public override string ToString() => $"A: {A}, B: {B}, C: {C}";
    }

Doing this (with NUnit 3.8 installed) produces the sample output above.

(Caveat: Charlie led the NUnit team for about 13 years and says this can't be done, so even if I've understood the question correctly it's possible that this technique is inadvisable; use at your own risk! But I hope it helps.)

Edit: Please see comments below, especially Charlie's note that this approach should only be taken with test classes, not production classes.

Upvotes: 2

Charlie
Charlie

Reputation: 13746

You need to consider the sequence of execution...

  1. Your TestFixtureSource runs to define what fixture instances will be created.
  2. If you have any TestCaseSources, that code is run to define what tests will be added to the fixture.

*** At this point, the structure of your tests (namespaces, fixtures, test cases) is completely defined and can't be changed. Any overridden test names are already in place.

  1. At this point, the tests start to run. In the console runner, it happens immediately. In a GUI or under VS it happens when the user clicks run. In the second case, it may happen multiple times.

  2. Any OneTimeSetup runs

  3. For each test in the fixture

    • Setup Runs
    • The test itself runs (provided Setup didn't throw)
    • Teardown Runs
  4. Any OneTimeTeardown runs

Key thing here is the break between loading (aka creating or discovering) the tests and running them. Nothing can happen in steps 4 - 6 to change what was done in steps 1 and 2.

Regarding changing the name... It's a constraint within NUnit that users may only change the name of test cases. The part that comes before the test name ... including the displayed name of the fixture ... is invariable because NUnit relies on it. Changing that would give us a different framework, or at least a version of NUnit with breaking changes.

Upvotes: 1

Related Questions