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未生效
两个核心错误导致逻辑未执行:
- 构造函数名不匹配:类名为
InfoClassCustomization,但构造函数写的是ArchivedOrderLineItemInfoCustomization,导致自定义类无法被正确初始化,逻辑根本没运行。 - 属性名赋值错误:代码中给
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
相关产品推荐
相关产品推荐

