如何用AutoFixture为私有Setter属性填充测试数据并测试Order类方法?
如何为带有私有Setter的Order属性填充自定义测试数据并测试ConfirmDownloadInvoiceDate方法
当然可以实现这个需求!你之前尝试的ISpecimenBuilder思路是对的,只是可能写法上没踩对关键点。下面给你几种可行的方案,从简单直接到自动化定制都有:
方法一:直接用反射设置属性(最快捷)
如果只是在单个测试里需要定制这个属性,直接用反射修改是最省事的,不用额外写自定义类:
var fixture = new Fixture(); var order = fixture.Create<Order>(); // 定位到目标属性的私有Setter并赋值 var dateProperty = typeof(Order).GetProperty( nameof(Order.ConfirmationDownloadInvoiceDate), BindingFlags.Public | BindingFlags.Instance); var privateSetter = dateProperty.GetSetMethod(nonPublic: true); privateSetter.Invoke(order, new object[] { new DateTime(2024, 1, 1) }); // 开始测试方法 var mockTimeProvider = new Mock<IDateTimeProvider>(); mockTimeProvider.Setup(tp => tp.Now()).Returns(new DateTime(2024, 2, 1)); order.ConfirmDownloadInvoiceDate(mockTimeProvider.Object); // 断言:因为属性已有值,所以不会被更新 Assert.Equal(new DateTime(2024, 1, 1), order.ConfirmationDownloadInvoiceDate);
方法二:自定义ISpecimenBuilder实现自动填充
如果你希望每次用fixture.Create<Order>()时都自动设置这个私有属性,那可以写一个正确的ISpecimenBuilder实现。核心是先让AutoFixture创建完整的Order实例,再通过反射修改目标属性——这可能是你之前没做到的点:
public class OrderDateCustomization : ISpecimenBuilder { private readonly DateTime? _customInvoiceDate; // 允许传入自定义日期,默认值可选 public OrderDateCustomization(DateTime? customDate = null) { _customInvoiceDate = customDate ?? new DateTime(2024, 1, 1); } public object Create(object request, ISpecimenContext context) { // 只处理Order类型的请求,其他类型交给AutoFixture默认处理 var requestedType = request as Type; if (requestedType != typeof(Order)) { return new NoSpecimen(); } // 先让AutoFixture创建Order的其他属性(保持默认的自动填充行为) var order = context.Create<Order>(); // 反射修改私有属性 var dateProperty = typeof(Order).GetProperty( nameof(Order.ConfirmationDownloadInvoiceDate), BindingFlags.Public | BindingFlags.Instance); var privateSetter = dateProperty.GetSetMethod(nonPublic: true); privateSetter.Invoke(order, new object[] { _customInvoiceDate }); return order; } }
在测试里使用这个定制:
var fixture = new Fixture(); // 添加自定义构造器,指定你想要的测试日期 fixture.Customizations.Add(new OrderDateCustomization(new DateTime(2024, 1, 1))); // 现在创建的Order会自动带有你设置的私有属性值 var order = fixture.Create<Order>(); // 后续测试逻辑和上面一致 var mockTimeProvider = new Mock<IDateTimeProvider>(); mockTimeProvider.Setup(tp => tp.Now()).Returns(new DateTime(2024, 2, 1)); order.ConfirmDownloadInvoiceDate(mockTimeProvider.Object); Assert.Equal(new DateTime(2024, 1, 1), order.ConfirmationDownloadInvoiceDate);
方法三:用AutoFixture的Build API快速定制
如果你不想写完整的ISpecimenBuilder,又想在单个测试里简洁地定制属性,可以用AutoFixture的Build API配合反射:
var fixture = new Fixture(); var customDate = new DateTime(2024, 1, 1); var order = fixture.Build<Order>() .Do(orderInstance => { var dateProperty = typeof(Order).GetProperty( nameof(Order.ConfirmationDownloadInvoiceDate), BindingFlags.Public | BindingFlags.Instance); dateProperty.GetSetMethod(true).Invoke(orderInstance, new object[] { customDate }); }) .Create(); // 测试逻辑同上
为什么你之前的ISpecimenBuilder没效果?
大概率是你的实现没有先让AutoFixture创建完整的Order实例,而是试图直接构造属性值;或者没有正确获取到私有Setter的方法。上面的方案都是先保留AutoFixture对Order其他属性的自动填充能力,再针对性修改私有属性,这样就能正常工作了。
内容的提问来源于stack exchange,提问作者Adrian Bartosz
相关产品推荐
相关产品推荐

