如何在AutoFixture中忽略基类所有实现类的指定属性
解决AutoFixture生成BaseItem派生类时忽略Language属性的问题
这个问题我之前也碰到过——AutoFixture默认会递归填充所有可访问的属性,哪怕是基类的属性,所以只自定义基类有时候不会自动覆盖派生类的行为。下面给你两种靠谱的解决方案,第二种更适合后续新增派生类的场景:
方案1:全局自定义(推荐,支持所有未来派生类)
这种方式通过自定义ISpecimenBuilder,拦截所有针对BaseItem及其子类的Language属性的生成请求,直接告诉AutoFixture跳过这个属性的填充。
首先创建一个自定义的Specimen Builder:
using AutoFixture; using AutoFixture.Kernel; using System.Reflection; public class IgnoreLanguagePropertyBuilder : ISpecimenBuilder { public object Create(object request, ISpecimenContext context) { // 检查当前请求是否是BaseItem(或其子类)的Language属性 var propertyInfo = request as PropertyInfo; if (propertyInfo != null && propertyInfo.DeclaringType.IsAssignableTo(typeof(BaseItem)) && propertyInfo.Name == nameof(BaseItem.Language)) { // 返回OmitSpecimen,告诉AutoFixture不要填充这个属性 return new OmitSpecimen(); } // 不是目标属性,交给其他Builder处理 return new NoSpecimen(); } }
然后在创建Fixture的时候,把这个Builder添加到自定义集合里:
var fixture = new Fixture(); // 注册自定义Builder fixture.Customizations.Add(new IgnoreLanguagePropertyBuilder()); // 现在创建ItemA/ItemB时,Language属性会保持默认值(值类型是default,引用类型是null) var itemA = fixture.Create<ItemA>(); var itemB = fixture.Create<ItemB>();
这个方案的好处是一劳永逸——后续新增任何BaseItem的派生类,都不需要额外修改代码,Language属性都会被自动忽略。
方案2:针对单个类型自定义(适合临时场景)
如果只是针对现有几个派生类做处理,可以直接用Customize<T>方法,明确忽略Language属性:
var fixture = new Fixture(); // 针对BaseItem自定义,保留Id属性,忽略其他(包括Language) fixture.Customize<BaseItem>(c => c.OmitAutoProperties().With(x => x.Id)); // 针对ItemA自定义,继承BaseItem的配置并保留自身Name属性 fixture.Customize<ItemA>(c => c.FromFactory(() => fixture.Create<BaseItem>().As<ItemA>()) .With(x => x.Name)); // 同理处理ItemB fixture.Customize<ItemB>(c => c.FromFactory(() => fixture.Create<BaseItem>().As<ItemB>()) .With(x => x.Path));
不过这种方式的缺点是,每次新增BaseItem的派生类,都要重复写一遍自定义逻辑,不如第一种方案灵活。
原理说明
OmitSpecimen是AutoFixture的内置标记,当Builder返回这个值时,AutoFixture会跳过该属性的自动填充,让属性保持其类型的默认值(比如枚举的默认是第一个枚举成员,引用类型是null)。而IgnoreLanguagePropertyBuilder会全局拦截所有符合条件的属性请求,确保所有BaseItem的子类都能生效。
内容的提问来源于stack exchange,提问作者adelb
相关产品推荐
相关产品推荐

