Reputation: 11
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.
Upvotes: 0
Views: 602
Reputation: 1672
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 :)
Upvotes: 1
Reputation: 13681
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);
}
Upvotes: 1