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

C# XUnit中使用AutoFixture创建自定义InfoClass对象失败的问题

问题分析与解决方案

需求背景

需要创建InfoClass的模拟对象,要求:

  • InfoID从指定字典ItemsMap的列表中随机选取
  • 根据InfoID所属的类别,为EDDetails或EVDetails生成赋值,另一字段保持为空

基础代码如下:

public class InfoClass
{
    public InfoClass()
    {
        this.InfoID = string.Empty;
        this.EDDetails = string.Empty;
        this.EVDetails = string.Empty;
    }
    public int ID { get; set;}
    public string InfoID { get; set;}
    public string EDDetails { get; set; }
    public string EVDetails { get; set; }
}

private static Dictionary<string, List<string>> ItemsMap = new Dictionary<string, List<string>>
{
    { Constants.ITEMCATEGORY_ED, new List<string> { "XYZ", "IDASAS" } },
    { Constants.ITEMCATEGORY_ERANDRE, new List<string> { "SAS", "PQR" } }
};

现有方案的问题

方案一:自定义ICustomization未生效

两个核心错误导致逻辑未执行:

  1. 构造函数名不匹配:类名为InfoClassCustomization,但构造函数写的是ArchivedOrderLineItemInfoCustomization,导致自定义类无法被正确初始化,逻辑根本没运行。
  2. 属性名赋值错误:代码中给o.ReportID赋值,但InfoClass的对应属性是InfoID,等于没给目标属性赋值,所以看起来没生效。

方案二:Do方法内重新赋值对象无效

AutoFixture的.Do(o => ...)是对已创建的默认对象执行修改操作,你在Do里把局部变量o重新赋值为GetItemInfo()返回的新对象,这只是改变了局部变量的引用,AutoFixture最终返回的还是最初创建的默认对象,所以自定义数据会丢失。

修正后的正确方案

修复方案一的问题

修正构造函数名和属性赋值错误,同时优化随机逻辑:

public class InfoClassCustomization : ICustomization
{
    private readonly Dictionary<string, List<string>> ItemsMap;
    private readonly Random _random = new Random(); // 类级别实例化,避免重复随机

    // 构造函数名与类名一致
    public InfoClassCustomization(Dictionary<string, List<string>> ItemsMap)
    {
        this.ItemsMap = ItemsMap;
    }

    public virtual void Customize(IFixture fixture)
    {
        fixture.Customize<InfoClass>(c => c
            .Do(o =>
            {
                // 展平字典为键值元组列表
                var keyValueList = ItemsMap.SelectMany(kvp => kvp.Value.Select(v => (Key: kvp.Key, Value: v))).ToList();
                var infoIdPair = keyValueList[_random.Next(keyValueList.Count)];

                // 赋值正确的属性InfoID
                o.InfoID = infoIdPair.Value;

                // 根据类别赋值对应字段,清空另一个字段
                switch (infoIdPair.Key)
                {
                    case Constants.ITEMCATEGORY_ED:
                        o.EDDetails = JsonConvert.SerializeObject(fixture.Create<EdDetails>());
                        o.EVDetails = string.Empty;
                        break;
                    case Constants.ITEMCATEGORY_ERANDRE:
                        o.EVDetails = JsonConvert.SerializeObject(fixture.Create<EvDetails>());
                        o.EDDetails = string.Empty;
                        break;
                }
            }));
    }
}

// 使用方式
var fixture = new Fixture().Customize(new InfoClassCustomization(ItemsMap));
var obj = fixture.Create<InfoClass>();

更简洁的FromFactory方案

如果想完全自定义对象创建流程,避免AutoFixture默认赋值的干扰,使用FromFactory直接生成目标对象:

public class InfoClassCustomization : ICustomization
{
    private readonly Dictionary<string, List<string>> ItemsMap;
    private readonly Random _random = new Random();

    public InfoClassCustomization(Dictionary<string, List<string>> ItemsMap)
    {
        this.ItemsMap = ItemsMap;
    }

    public void Customize(IFixture fixture)
    {
        fixture.Customize<InfoClass>(c => c
            .FromFactory(() =>
            {
                var keyValueList = ItemsMap.SelectMany(kvp => kvp.Value.Select(v => (Key: kvp.Key, Value: v))).ToList();
                var infoIdPair = keyValueList[_random.Next(keyValueList.Count)];

                // 直接创建并配置对象
                var info = new InfoClass
                {
                    InfoID = infoIdPair.Value
                };

                switch (infoIdPair.Key)
                {
                    case Constants.ITEMCATEGORY_ED:
                        info.EDDetails = JsonConvert.SerializeObject(fixture.Create<EdDetails>());
                        break;
                    case Constants.ITEMCATEGORY_ERANDRE:
                        info.EVDetails = JsonConvert.SerializeObject(fixture.Create<EvDetails>());
                        break;
                }

                return info;
            }));
    }
}

关键注意事项

  • 确保类名与构造函数名一致,否则自定义逻辑无法被AutoFixture识别。
  • 严格匹配属性名,避免赋值到错误字段。
  • 使用.Do()时,只能修改已存在对象的属性,不能重新赋值对象引用;完全自定义对象用.FromFactory()更合适。
  • 不要在每次随机操作时实例化Random,类级别保留一个实例可避免短时间内重复生成相同的随机结果。

内容的提问来源于stack exchange,提问作者vinay

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 15:58:15