可以将测试类上的字段用作XUnit Theory的参数吗?

huangapple go评论51阅读模式
英文:

Can I use a field on my test class as a parameter to an XUnit Theory?

问题

I like to define named test data as fields on my test class, and sometimes multiple are run against the same test using a Theory.

我喜欢在我的测试类上将命名的测试数据定义为字段,并有时使用 Theory 运行多个测试。

I've used MemberData but this appears to only support IEnumerable so I have to write somewhat ugly wrapper enumerables:

我曾使用 MemberData,但它似乎只支持 IEnumerable,因此我不得不编写有点丑陋的包装可枚举对象:

public class MyTests
{
    public static string s1 = "test";
    public static string s2 = "bees";
    public static string s3 = "pear";
    public static string s4 = "joke";

    public static IEnumerable<object[]> Strings => new List<object[]>
    {
        new object[] { s1 },
        new object[] { s2 },
        new object[] { s3 },
        new object[] { s4 },
    };

    [Theory]
    [MemberData(nameof(Strings))]
    public void ContainsLetterE(string s)
    {
        s.Should().Contain("e");
    }
}

What I am wondering is if there is a way to use explicit fields/properties, along the lines of:

我想知道是否有一种方式可以使用显式字段/属性,类似于以下方式:

public class MyTests
{
    public static string s1 = "test";
    public static string s2 = "bees";
    public static string s3 = "pear";
    public static string s4 = "joke";

    [Theory]
    [Field(nameof(s1))]
    [Field(nameof(s2))]
    [Field(nameof(s3))]
    [Field(nameof(s4))]
    public void ContainsLetterE(string s)
    {
        s.Should().Contain("e");
    }
}

Is this possible at all, or does it have to be IEnumerable based?

这是否有可能,或者必须基于 IEnumerable

英文:

I like to define named test data as fields on my test class, and sometimes multiple are run against the same test using a Theory.
I've used MemberData but this appears to only support IEnumerable so I have to write somewhat ugly wrapper enumerables:

public class MyTests
{
	public static string s1 = &quot;test&quot;;
	public static string s2 = &quot;bees&quot;;
	public static string s3 = &quot;pear&quot;;
	public static string s4 = &quot;joke&quot;;

	public static IEnumerable&lt;object[]&gt; Strings =&gt; new List&lt;object[]&gt;
	{
		new object[] { s1 },
		new object[] { s2 },
		new object[] { s3 },
		new object[] { s4 },
	};

	[Theory]
	[MemberData(nameof(Strings))]
	public void ContainsLetterE(string s)
	{
		s.Should().Contain(&quot;e&quot;);
	}
}

What I am wondering is if there is a way to use explicit fields/properties, along the lines of:

public class MyTests
{
	public static string s1 = &quot;test&quot;;
	public static string s2 = &quot;bees&quot;;
	public static string s3 = &quot;pear&quot;;
	public static string s4 = &quot;joke&quot;;

	[Theory]
	[Field(nameof(s1))]
	[Field(nameof(s2))]
	[Field(nameof(s3))]
	[Field(nameof(s4))]
	public void ContainsLetterE(string s)
	{
		s.Should().Contain(&quot;e&quot;);
	}
}

Is this possible at all, or does it have to be IEnumerable based?

答案1

得分: 1

InlineDataAttribute 意味着我必须在多个地方重新键入相同的内容,这会增加出错的风险。

如果您将这些内容定义为常量,那么您可以提供这些常量本身,而无需键入值。

如果您不想这样做,那么您可以使用大量的 xunit 自定义方法,尽管需要一些工作。

  1. 您可以定义自己的属性,该属性继承自 DataAttribute(参见此处的示例1)。

    public override IEnumerable<object[]> GetData(MethodInfo testMethod)
    ...
    

然后,如果将此属性添加到您的测试中,在 MethodInfo testMethod 中,您将获得有关测试本身、基类等的完整信息,包括应用的自定义属性。您可以考虑的一个工作流示例是使用此新数据属性(用于测试本身),并与类似 ValuesAttribute 的不同自定义属性一起使用:

[AttributeUsage(AttributeTargets.Parameter)]
public sealed class ValuesAttribute : Attribute
{
    private readonly object[] _values;

    public ValuesAttribute(params object[] values)
    {
        _values = values;
    }

    public object[] GenerateValues()
    {
        return _values;
    }
}

这将允许您在测试中读取自定义输入并在运行时生成测试用例。如果我正确理解您的需求,示例如下:

[Theory]
[CustomDataAttribute]
public void Test(Values(nameof(s1), nameof(s2)) OutputType value)
...

CustomDataAttribute 内部,您需要通过反射从参数中读取 ValuesAttribute 值,然后通过反射从测试类本身读取所需的字段,并最终正确填充输出 public override IEnumerable<object[]> 为 OutputType。

  1. 实现自己的 MemberData 逻辑,如下所示。注意:上面的方法已经简化了 MemberDataAttributeBase,因为 MemberDataAttributeBase 继承自 DataAttribute

    public class CustomMemberData : MemberDataAttributeBase
    {
       public CustomMemberData(string memberName, object[] parameters) : base(memberName, parameters)
       {
       }
    
       protected override object[] ConvertDataItem(MethodInfo testMethod, object item) => /*您的逻辑*/;
    }
    

还有更多方法,但思路是,如果需要,您可以在 xunit 工作流中定制几乎所有内容 可以将测试类上的字段用作XUnit Theory的参数吗? 但您需要为此编写一些基础架构。

英文:

Writing it as answer since it's quite big.

>InlineDataAttribute means I have to re-type the same in several places which increases risk of error.

if you define this as constants then you can provide these constants itself instead of typing values.

if you don't want doing it, then you have a huge number of xunit customization approaches that will require some work though.

  1. You can define your own attribute inherited from DataAttribute (See example here).

    public override IEnumerable&lt;object[]&gt; GetData(MethodInfo testMethod)
    ...
    

Then if you add this attribute to your test, in MethodInfo testMethod you will have full information about test itself, base class and so on, including applied custom attributes. One workflow example that you can consider is using this new data attribute (for a test itself) with a different custom attribute like ValuesAttribute similar to:

[AttributeUsage(AttributeTargets.Parameter)]
public sealed class ValuesAttribute : Attribute
{
    private readonly object[] _values;

    public ValuesAttribute(params object[] values)
    {
        _values = values;
    }

    public object[] GenerateValues()
    {
        return _values;
    }
}

which will allow you reading custom input for a test and generate test cases in flight. If I understand what you're asking correctly it will be:

[Theory]
[CustomDataAttribute]
public void Test(Values(nameof(s1), nameof(s2)) OutputType value)
...

where inside CustomDataAttribute you will need to read ValuesAttribute value from parameter via reflection, then read fields you need from the test class itself with reflection too and eventually properly fill output here public override IEnumerable&lt;object[]&gt; as OutputType.

  1. Implement your own logic for MemberData like below. Note: it's simplified above approach since MemberDataAttributeBase is inherited from DataAttribute

    public class CustomMemberData : MemberDataAttributeBase
    {
       public CustomMemberData(string memberName, object[] parameters) : base(memberName, parameters)
       {
       }
    
       protected override object[] ConvertDataItem(MethodInfo testMethod, object item) =&gt; /*your logic*/;
    }
    

There are more ways, but the idea is that you can customize almost everything in xunit workflow if you need to 可以将测试类上的字段用作XUnit Theory的参数吗? But you need to write some infrustructure for it.

huangapple
  • 本文由 发表于 2023年3月3日 19:32:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/75626550.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定