You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否将测试类中的字段作为XUnit Theory的参数使用?

XUnit中能否直接用字段作为Theory的测试数据?

我习惯将测试数据定义为测试类的静态字段,使用XUnit的Theory特性运行多组测试。之前用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");
    }
}

想知道有没有办法直接引用字段,类似这样的写法:

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");
    }
}

这种方式是否可行,还是必须基于IEnumerable类型实现?


解答

XUnit原生并没有提供[Field]这类直接引用字段的特性,但有几种方案可以避免繁琐的IEnumerable包装:

1. 使用[InlineData]直接传入值

如果测试数据不需要复用,直接用[InlineData]是最简单的方式,不需要额外定义字段:

public class MyTests
{
    [Theory]
    [InlineData("test")]
    [InlineData("bees")]
    [InlineData("pear")]
    [InlineData("joke")]
    public void ContainsLetterE(string s)
    {
        s.Should().Contain("e");
    }
}

2. 自定义[FieldData]特性

可以自己实现一个类似需求的特性,通过反射读取指定静态字段的值:

using System;
using System.Collections.Generic;
using System.Reflection;
using Xunit;
using Xunit.Sdk;

public class FieldDataAttribute : DataAttribute
{
    private readonly string _fieldName;

    public FieldDataAttribute(string fieldName)
    {
        _fieldName = fieldName;
    }

    public override IEnumerable<object[]> GetData(MethodInfo testMethod)
    {
        var field = testMethod.DeclaringType.GetField(_fieldName, BindingFlags.Public | BindingFlags.Static);
        if (field == null)
            throw new ArgumentException($"未找到字段 {_fieldName}");
        
        yield return new[] { field.GetValue(null) };
    }
}

之后就可以像你期望的那样使用:

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

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

注:这个自定义特性仅处理public static字段,若需要支持其他访问权限的字段,可以修改BindingFlags参数扩展逻辑。

3. 简化MemberData的写法

如果还是想用MemberData,可以用LINQ简化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[] { s1, s2, s3, s4 }.Select(value => new object[] { value });

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

内容的提问来源于stack exchange,提问作者Mr. Boy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 22:00:35