Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ITestDataSource.GetData() could be easier to use with custom objects #246

Closed
Tracked by #910
Frannsoft opened this issue Aug 18, 2017 · 5 comments
Closed
Tracked by #910

Comments

@Frannsoft
Copy link

Frannsoft commented Aug 18, 2017

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.

AB#1408171

@AbhitejJohn
Copy link
Contributor

@Frannsoft: That is a fair ask. Tagging @harshjain2 to get his thoughts on this.

@pvlakshm pvlakshm added enhancement Help-Wanted The issue is up-for-grabs, and can be claimed by commenting and removed Discussion labels Nov 2, 2017
@aaronhudon
Copy link

Strange that ITestDataSource wasn't more like ITestDataSource

@Justin-Lloyd
Copy link

Justin-Lloyd commented Nov 23, 2018

@harshjain2 Is there any traction on this? Seems like a fairly obvious functional gap for unit testing.

@hartmair
Copy link

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.
}

@Youssef1313
Copy link
Member

This was fixed by #4389. @Frannsoft Please, let us know if you see further issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants