如何为AutoFixture自定义ICloneable的Clone方法以避免类型转换异常?
解决AutoFixture中ICloneable对象Clone方法的类型转换异常问题
在单元测试中用AutoFixture填充对象时,若对象层级包含ICloneable类型,会遇到默认生成的Clone()方法返回System.Object实例,导致代码抛出InvalidCastException的问题。由于测试是通用实现,无法提前知晓具体类型,需要给AutoFixture配置通用规则,让所有ICloneable类型的Clone()方法返回匹配声明类型的实例。
可复现代码
[TestClass] public class CloneMock { public abstract class Foo : ICloneable { public abstract object Clone(); } public class Bar { public Foo Foo { get; set; } public void CloneTheFoo() { this.Foo = (Foo)this.Foo.Clone(); // 此处抛出异常 } } [TestMethod] public void SomeTest() { var f = new Fixture(); f.Customize(new AutoMoqCustomization()); var c = f.Create<Bar>(); c.CloneTheFoo(); } }
报错信息
System.InvalidCastException:
无法将类型为“Castle.Proxies.ObjectProxy”的对象强制转换为类型“Foo”。
在 Zeiss.Micro.LSM.Tests.Utilities.CloneMock.Bar.CloneTheFoo()
解决方案
通过自定义AutoFixture的ISpecimenBuilder,拦截所有ICloneable类型的Mock创建,强制Clone()方法返回当前类型的实例。
1. 实现自定义SpecimenBuilder
public class CloneableMockBuilder : ISpecimenBuilder { public object Create(object request, ISpecimenContext context) { var targetType = request as Type; // 仅处理实现ICloneable的类型请求 if (targetType == null || !typeof(ICloneable).IsAssignableFrom(targetType)) { return new NoSpecimen(); } // 动态创建对应类型的Mock var mockGenericType = typeof(Mock<>).MakeGenericType(targetType); var mockInstance = Activator.CreateInstance(mockGenericType); // 获取Mock的Object属性值(即被Mock的实例) var mockObject = mockGenericType.GetProperty("Object").GetValue(mockInstance); // 配置Clone方法返回当前Mock实例 var setup = mockGenericType.GetMethod("Setup") .MakeGenericMethod(typeof(object)) .Invoke(mockInstance, new object[] { () => ((ICloneable)mockObject).Clone() }); setup.GetMethod("Returns") .Invoke(setup, new object[] { mockObject }); return mockObject; } }
2. 在Fixture中注册自定义Builder
修改测试中的Fixture初始化逻辑,添加自定义Builder:
[TestMethod] public void SomeTest() { var fixture = new Fixture(); var autoMoqCustomization = new AutoMoqCustomization(); fixture.Customize(autoMoqCustomization); // 注册自定义的ICloneable处理规则 fixture.Customizations.Add(new CloneableMockBuilder()); var bar = fixture.Create<Bar>(); bar.CloneTheFoo(); // 不再抛出类型转换异常 }
原理说明
这个自定义Builder会拦截所有实现ICloneable的类型请求,使用Moq动态生成对应类型的Mock,并强制Clone()方法返回该Mock的实际实例。这样当代码中把Clone()的返回值转换为声明类型时,就不会出现类型不匹配的问题,完美适配代码库的通用模式。
内容的提问来源于stack exchange,提问作者codymanix
相关产品推荐
相关产品推荐

