如何通过AutoFixture Attributes获取FakeTimeProvider引用以避免类型转换
问题:如何避免测试中每次强制转换FakeTimeProvider的操作?
我有一个依赖TimeProvider的Service类,目前用AutoFixture和Moq配置FakeTimeProvider的方式如下:
public class FakeTimeProviderCustomization : ICustomization { public void Customize(IFixture fixture) { fixture.Customize<TimeProvider>(c => c.FromFactory(() => new FakeTimeProvider())); } } public class MoqAutoDataAttribute : AutoDataAttribute { public MoqAutoDataAttribute() : base(() => new Fixture() .Customize(new AutoMoqCustomization()) .Customize(new FakeTimeProviderCustomization()) ) { } } [Theory, MoqAutoData] public async Task Test_TimeRelatedStuff( [Frozen] TimeProvider timeProvider, Service sut) { // ... var fakeTimeProvider = timeProvider as FakeTimeProvider; // ... }
请问能否避免每次都需要将timeProvider强制转换为FakeTimeProvider的操作?
解决方案
当然可以,有几种简单方式能省去强制转换的步骤:
方法1:直接在测试方法中注入FakeTimeProvider并标记[Frozen]
修改测试方法的参数,直接声明FakeTimeProvider类型并加上[Frozen]特性,AutoFixture会自动复用你之前定制好的实例(因为已将TimeProvider的实现替换为FakeTimeProvider):
[Theory, MoqAutoData] public async Task Test_TimeRelatedStuff( [Frozen] FakeTimeProvider fakeTimeProvider, Service sut) { // 直接使用fakeTimeProvider,无需转换 fakeTimeProvider.SetUtcNow(DateTimeOffset.Parse("2024-01-01")); // ... }
方法2:扩展Customization,让Fixture支持直接创建FakeTimeProvider
修改FakeTimeProviderCustomization,同时注册FakeTimeProvider的实例,确保Fixture能直接解析该类型:
public class FakeTimeProviderCustomization : ICustomization { public void Customize(IFixture fixture) { var fakeTimeProvider = new FakeTimeProvider(); // 注册FakeTimeProvider实例,同时将其作为TimeProvider的实现 fixture.Inject(fakeTimeProvider); fixture.Inject<TimeProvider>(fakeTimeProvider); } }
这样在测试中无论是请求TimeProvider还是FakeTimeProvider,都会拿到同一个实例,测试方法里直接用FakeTimeProvider类型即可。
方法3:自定义专属的AutoData属性(可选)
如果需要更统一的配置,可以在自定义AutoData属性中直接完成FakeTimeProvider的注入,进一步简化测试代码:
public class MoqWithFakeTimeAutoDataAttribute : AutoDataAttribute { public MoqWithFakeTimeAutoDataAttribute() : base(() => { var fixture = new Fixture().Customize(new AutoMoqCustomization()); var fakeTime = new FakeTimeProvider(); fixture.Inject(fakeTime); fixture.Inject<TimeProvider>(fakeTime); return fixture; }) { } }
然后测试方法直接用这个新属性:
[Theory, MoqWithFakeTimeAutoData] public async Task Test_TimeRelatedStuff( FakeTimeProvider fakeTimeProvider, Service sut) { // 直接使用 // ... }
内容的提问来源于stack exchange,提问作者Ivan Petrov
相关产品推荐
相关产品推荐

