IEnumerable to TheoryData
Asked Answered
M

4

3

Is there an easy way to convert an IEnumerable to TheoryData?

For example, if I want to pass the set of enumeration options, I'm looking for something like:

IEnumerable<TheoryData<MyEnum>> dbEnumOptions
  = Enum.GetValues(typeof(MyEnum))
    .Cast<MyEnum>()
    .ToTheoryData(v => v);

In general though, this would help with getting fluent LINQ stuff into TheoryData. Currently, we're working around this by just using MemberData, but would prefer TheoryData's better compile-time type-checking.

Midsummer answered 4/8, 2022 at 0:38 Comment(3)
Extension methods aren't magic. They are just code, written by someone. You can write your own ToTheoryData<T> extension method that does what you want. Internally, it could create the TheoryData<T> object, transform each source item based on the expression you provide, add those results and then return the object.Newkirk
Right, but I'd like to avoid reinventing the wheel if it's already hiding in the library somewhere, particularly on a pseudo-variadic generic like TheoryData. It seems like creating TheoryData from a list would be a relatively common use-case.Midsummer
How are you using the TheoryData so that there is compiler time type checking? TheoryData ultimately returns a set of objects (object[]) on each iteration so it can be used with aClassData attribute, they could still have the wrong type. class TestSpecificData : TheoryData<string, int> and class TestSpecificData2 : TheoryData<int, string> both compiled just fine when used as data in this test: [ClassData(typeof(TestSpecificData))] [ClassData(typeof(TestSpecificData2))] public void UnitTest1(string a, int b) but fail at runtime.Bloomy
E
1

You can create TheoryData<MyEnum> and use the Add method to add items to your TheoryData like this, and then use it

Note: this this will be created for each method execution

public static TheoryData<MyEnum> EnumValues
{
    get
    {
        var data = new TheoryData<MyEnum>();

        foreach (MyEnum item in Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>())
        {
            data.Add(item);
        }

        return data;
    }
}

and use it like this

[Theory]
[MemberData(nameof(EnumValues))]
public void Get_Input_Success(MyEnum enumValue)
{

}
Eddo answered 14/12, 2023 at 12:40 Comment(1)
From .NET 5, you should use Enum.GetValues<MyEnum>() and no cast instead of Enum.GetValues(typeof(MyEnum)). But we do not know if the asker is on (legacy) .NET Framework.Broadloom
P
1

The easiest and safest way is to use the constructor accepting an IEnumerable<T>. With modern C# this looks like this:

public class TestClass
{
    public static readonly TheoryData<MyEnum> TestData = new(Enum.GetValues<MyEnum>());

    [Theory]
    [MemberData(nameof(TestData))]
    public void TestMethod(MyEnum value)
    {
        // Arrange, Act, Assert
    }
}

Be aware that MyEnum needs to be defined in the project where TestClass is defined, otherwise the test will just not run. It seems like this is a bug in xUnit or test explorer / runner. In VS you can see this because the test explorer cannot discover the tests when MyEnum is defined in another project.

Often the enum is defined in another project, so we need a solution for this case.

Currently, I use TheoryData<string> to pass the enum values as string to the test method and then convert this string back to the actual enum values.

public class TestClass
{
    public static readonly TheoryData<string> TestData = new(Enum.GetNames<MyEnum>());

    [Theory]
    [MemberData(nameof(TestData))]
    public void TestMethod(string value)
    {
        // Arrange
        var valueTyped = Enum.Parse<MyEnum>(value);

        // Act, Assert
    }
}
Polariscope answered 2/2, 2024 at 9:29 Comment(0)
B
0

There is nothing built-in that does this as far as I can see, but you can easily define your own extension method to do it:

internal static class EnumerableExtensions
{
    public static TheoryData<T> ToTheoryData<T>(this IEnumerable<T> enumerable)
    {
        var result = new TheoryData<T>();
        foreach (var element in enumerable)
        {
            result.Add(element);
        }

        return result;
    }
}

Usage:

TheoryData<MyEnum> dbEnumOptions = Enum.GetValues<MyEnum>().ToTheoryData();

If you need some kind of transformation, just use Select:

TheoryData<string> dbEnumOptions = Enum.GetValues<MyEnum>().Select(x => x.ToString()).ToTheoryData();
Brentonbrentt answered 31/12, 2023 at 13:3 Comment(0)
T
-1

It is possible to pass enumerable theorydata; however, this involves setting up classAttribute with a declaration for an overridable GetData method.

public class ClassAutoMoqDataAttribute : CompositeDataAttribute
{
    public ClassAutoMoqDataAttribute(Type className)
        : base(new ClassDataAttribute(className), new AutoMoqDataAttribute())
    {
    }
    
    public override IEnumerable<object[]> GetData(MethodInfo testMethod)
    {
        if (testMethod == null)
        {
            throw new ArgumentNullException(nameof(testMethod));
        }
        var theoryAttribute = Attributes.FirstOrDefault();
        var data = theoryAttribute.GetData(testMethod);

        var autoDataAttribute = Attributes.ElementAt(1);
        if (autoDataAttribute == null)
        {
            yield break;
        }

        foreach (var datum in data)
        {
            var autoData = autoDataAttribute.GetData(testMethod).ToArray()[0];

            for (var i = 0; i < datum.Length; i++)
            {
                autoData[i] = datum[i];
            }

            yield return autoData;
        }
    }
}
Theo answered 16/11, 2023 at 15:43 Comment(3)
There's no need for a custom attribute.Papandreou
above solution would work for broader use cases like passing automoq dependencies and theory data together.Theo
This isn't an AutoMoq question at allPapandreou

© 2022 - 2025 — McMap. All rights reserved.