NUnit custom attribute for each [TestCase]

61 views Asked by At

in my UI tests I created Custom Attribute [TestCaseId] to be used in tests:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class TestCaseIdAttribute : PropertyAttribute
{
    public TestCaseIdAttribute(int testCaseId)
      : base(testCaseId.ToString()) { }
}

which I use like here:
(I use it in [TearDown] to mark Azure Tests as Automated but it's not relevant here)

        [TestCaseId(123456)]
        [Test]
        public void TestScenario_1()
        {
            Assert.That(true);
        }

and this works as intended, I access TestCaseId via: _testContext.Test.Properties["TestCaseId"].First(); to retrieve the value.

When I use [TestCase] attribute instead of [Test], like here:

        [TestCaseId(123456)]
        [TestCase(1, 2)]
        public void TestScenario_1(int arg0, int arg1)
        {
            Assert.That(arg0 != arg1);
        }

I access via: _testContext.Test.Method.MethodInfo.CustomAttributes.FirstOrDefault(a => a.AttributeType.Name == "TestCaseIdAttribute").ConstructorArguments[0].Value;

However, I don't know how to read TestCaseId having different custom attribute [TestCaseId] for each [TestCase], like here:

        [TestCaseId(123456)]
        [TestCase(1, 2)]
        [TestCaseId(666666)]
        [TestCase(9, 8)]
        public void TestScenario_1(int arg0, int arg1)
        {
            Assert.That(arg0 != arg1);
        }

Any idea how to match [TestCaseId] attribute to each [TestCase]?

I tried to match an object from _testContext.Test.Method.MethodInfo.CustomAttributes to my TestName but there is no any Test Name included there. It would be best to have a property assigned to [TestCase] the same way as it is to [Test]. I use NUnit 4.

1

There are 1 answers

1
Charlie On

The Test attribute, as you know, generates a single test case inside your fixture. The TestCase attribute generates multiple test cases inside a test suite, also nested within the test fixture. That's why you had to use a different expression to retrieve our test case id when using TestCase.

NUnit understands this but .NET and C# do not. .NET only knows that there is an attribute but it knows nothing of test cases and the like. It can only place custom attributes on constructs it understands. Therefore each attribute applies to the next element in the code, which is allowed to accept an attribute.

In your last example, with two TestCase and two TestCaseId attributes, that next element that can take an attribute is the method. All four attributes apply to that method. You could reorder the four attributes in any order you liked and the outcome would be the same, because that order has no meaning to .NET.

When NUnit finds a TestCaseAttribute on a method it knows what to do: create a test case with the values and properties specified on that attribute. When it finds a PropertyAttribute on a method, it simply assigns that property. It receives no information from .NET to tell it you intended that property to apply to a particular test case.

So far, that's a lot of info about why it doesn't work. So how can you make it work? TestCaseAttribute, unfortunately, doesn't provide a way to set a property on the test case. You could (mis)use some other field like Description for this purpose, but I don't recommend it.

This is the point where I would switch to using TestCaseSourceAttribute, which provides a number of capabilities beyond what TestCaseAttribute supports. Your last example could become something like...

static TestCaseData[] Scenario_1_Cases
{
    new TestCaseData(1, 2) {Properties = { { "TestCaseId", "123456"} } };
    new TestCaseData(9, 8) {Properties = { { "TestCaseId", "666666"} } };
}

[TestCaseSource(nameof(Scenario_1_Cases)]
public void TestScenario_1(int arg0, int arg1
{
    Assert.That(arg0 != arg1);
}