0

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.

2 Answers 2

1

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);
}
1
  • thanks for very comprehensive answer. I have been pretty aware of the solution like yours however I have mostly [Test] attributes in my test suite and that's why I am looking for one, common solution for [Test] and [TestCase] scenarios.
    – czarls
    Commented Feb 9 at 10:33
1

I just had the same problem and found a solution (better one than using TestCaseSource IMHO).

Just create your own attribute that derives from TestCaseAttribute and add a property TestCaseId that adds its value to the base class's Properties property, like that:

class MyTestCaseAttribute : TestCaseAttribute
{
    private string? _testId;

    public MyTestCaseAttribute(params object[] args)
    : base(args)
    {
    }

    public string? TestCaseId
    {
        get => _testId;
        set
        {
            _testId = value;
            Properties["TestCaseId"] = new List<string> {value!};
        }
    }
}

And then you can use it on any test method like you would use any parameterized test, optionally adding the TestCaseId property value like that:

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

If you have many parameterized tests, it's easier to replace TestCase with MyTestCase than to switch them to TestCaseSource. In addition, if you have a common base class, you can define this attribute inside the base class and call it TestCaseAttribute, just like its base class (just in another namespace), and then you don't have to change anything in your existing tests, except of adding the new parameter :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.