When I use the [DynamicData]
attribute to provide an enumeration of primitve values, e.g. int
, then the Visual Studio Test Explorer shows one test for each value provided via dynamic data:
public static IEnumerable<object[]> PrimitiveTestData
{
get
{
yield return new object[] {1};
yield return new object[] {2};
yield return new object[] {3};
}
}
[TestMethod]
[DynamicData(nameof(PrimitiveTestData))]
public void TestWithPrimitiveTestData(int value)
{
Console.WriteLine($"value is {value}");
}
Screenshot from Test Explorer, note how 3 "subentries" are created, one for each value provided via dynamic data:
However, when providing complex/custom types via [DynamicData]
attribute, then no "subentries" are created:
public record CustomRecord(
decimal Number,
string Comment
);
public static IEnumerable<object[]> RecordTestData
{
get
{
yield return new object[] {
new CustomRecord(1m, "first")
};
yield return new object[] {
new CustomRecord(2m, "second")
};
yield return new object[] {
new CustomRecord(3m, "third")
};
}
}
[TestMethod]
[DynamicData(nameof(RecordTestData))]
public void TestWithRecordTestData(CustomRecord item)
{
Console.WriteLine($"item number is {item.Number} entries");
}
In this case, no "subentries" are created in the Test Explorer (you can see that as the triangle on the left side is not there):
How can I get subentries in the Test Explorer, when using complex/custom types? I need that to be able to debug only a single "subentry" / a test with only one of the entries provided via dynamic data (right click on the "subentry" in the Test Explorer, select Debug, which of course is not possible when there is no "subentry" created).
You'll have to declare and initialize your records like so:
public record CustomRecord
{
public decimal Number;
public string Comment = "";
}
public static IEnumerable<object[]> RecordTestData
{
get
{
yield return new object[] {
new CustomRecord { Number = 1m, Comment = "first" }
};
yield return new object[] {
new CustomRecord { Number = 2m, Comment = "second" }
};
yield return new object[] {
new CustomRecord { Number = 3m, Comment = "third" }
};
}
}
That is, not with the parametrized constructor new CustomRecord(...)
. Why expanding on such an item doesn't work with the test explorer is unclear to me. The feature might not have been fully developed yet as it was introduced in VS 2022.
Update
Another way to get the single test data entries is to use DynamicDataDisplayName on the test method attributes:
public static string GetTestDisplayName(MethodInfo methodInfo, object[] values)
{
var item = (CustomRecord)values[0];
return $"{methodInfo.Name}(Number = {item.Number}, Comment = {item.Comment})";
}
[TestMethod]
[DynamicData(nameof(RecordTestData), DynamicDataSourceType.Property,
DynamicDataDisplayName = nameof(GetTestDisplayName))]
public void TestWithRecordTestData(CustomRecord item)
{
Console.WriteLine($"item number is {item.Number} entries");
}
Which yields the same output as shown in the pic above. This comes in handy when working with structs or classes instead of records, e.g. when targeting .Net Framework which lacks records.