testfx
testfx copied to clipboard
ITestDataSource.GetData() could be easier to use with custom objects
Implementing ITestDataSource.GetData()
currently requires an IEnumerable<object[]>
to be returned. This is useful when wanting to return sets of primitives, but a bit tedious when the consumer wants to return more complex objects if I'm using this correctly.
Example:
[TestMethod]
[CarTestData]
public void ATest(Car carUnderTest)
{
//perform test.
}
public class Car
{
public string Model { get; set; }
public int Year { get; set; }
}
The way I've done this for now is by implementing ITestDataSource.GetData()
like so:
//config for the 'CarTestData' attribute:
class CarTestDataAttribute : Attribute, ITestDataSource
{
public IEnumerable<object[]> GetData(MethodInfo methodInfo)
{
return new List<object[]>
{
new object[]{new Car { Model = "Ford", Year = 1990}},
new object[]{new Car {Model = "Nissan", Year = 2000} }
};
}
}
Is there a better way I can do this? Or would it be possible to have a variant of ITestDataSource
that returns IEnumerable<object>
(or maybe IEnumerable<T>
) instead of IEnumerable<object[]>
?
I tried using DynamicDataAttribute
to accomplish this as well and ran into a similar 'issue' of IEnumerable<object[]>
as a required return value.
Environment
- Windows 7
- Visual Studio 2015
- MSTest.Framework = 1.20-beta
Thanks for your time.
@Frannsoft: That is a fair ask. Tagging @harshjain2 to get his thoughts on this.
Strange that ITestDataSource wasn't more like ITestDataSource<T>
@harshjain2 Is there any traction on this? Seems like a fairly obvious functional gap for unit testing.
I have used the following snippet
// see https://github.com/microsoft/testfx/blob/master/src/TestFramework/MSTest.Core/Attributes/DataRowAttribute.cs
public abstract class TestDataSourceAttribute : Attribute, ITestDataSource
{
/// <summary>Gets Or sets display name in test results for customization.</summary>
public string DisplayName { get; set; }
public abstract IEnumerable<object[]> GetData(MethodInfo methodInfo);
public virtual string GetDisplayName(MethodInfo methodInfo, object[] data)
{
if (!string.IsNullOrWhiteSpace(DisplayName))
{
return DisplayName;
}
else
{
return $"{methodInfo.Name} ({string.Join(",", data.AsEnumerable())})";
}
}
}
It is not possible to replace IEnumerable<object[]>
by IEnumerable<T>
because attributes must not be generic :/ So you would have to separate the attribute from the ITestDataSource
implementation like so:
// see https://github.com/xunit/xunit/blob/main/src/xunit.v3.core/ClassDataAttribute.cs
public class ClassDataAttribute : TestDataSourceAttribute
{
public Type Class { get; }
/// <param name="class">The class that provides the data.</param>
public ClassData(Type @class)
{
Class = @class;
}
public override IEnumerable<object[]> GetData(MethodInfo methodInfo)
{
if (Activator.CreateInstance(Class) is ITestDataSource dataSource)
{
return dataSource.GetData(methodInfo);
}
else
{
throw new Exception($"{Class.FullName} must implement IEnumerable<object[]> to be used as ClassData.");
}
}
}
Usage:
[DataTestMethod]
[ClassData(typeof(CarTestData))]
public void ATest(Car carUnderTest)
{
// perform test.
}