如何让Autofixture创建一个包含具有接口类型的属性的类型的实例?

时间:2012-10-18 07:33:38

标签: c# autofixture test-data

我有这样一个班级:

public class ViewModel
{
    public IPagination<Data> List { get; set; } // interface!
    public SearchFilter SearchFilter { get; set; }
    public string Test { get; set; }
}

public class SearchFilter
{
    public string Name { get; set; }
}

应在IPagination接口周围创建动态代理,代理应填充测试数据。 现在可以让AutoFixture创建一个ViewModel类型的实例吗? 请注意,我只知道运行时的类型(typeof(ViewModel))。

到现在为止我知道我可以这样做:

var context = new SpecimenContext(fixture.Compose());
var value = context.Resolve(new SeededRequest(typeof(ViewModel), null))

2 个答案:

答案 0 :(得分:4)

理论上,应该可以填充自动模拟实例的属性。

假设IPagination<T>类型的ViewModel属性定义为:

public interface IPagination<T>
{
    SearchFilter Property1 { get; set; }
    string Property2 { get; set; }
}

我们可以创建 ad-hoc 自动模拟自定义,例如MyCustomization

var fixture = new Fixture()
    .Customize(new MyCustomization());
var context = new SpecimenContext(fixture.Compose());

然后,以下调用将创建ViewModel 的实例(仅在运行时已知),提供IPagination<Data>和<的自动模拟实例strong>为属性赋值。

var value = context.Resolve(typeof(ViewModel));
// List -> {IPagination`1Proxy593314cf4c134c5193c0019045c05a80}
// List.Property1.Name -> "Namef71b8571-a1a0-421d-9211-5048c96d891b" 
// List.Property2 -> "f58cae65-b704-43ec-b2ce-582a5e6177e6"

MyCustomization

  

在应用此自定义之前,请记住,这应仅适用于此特定方案(因此说明中的 ad-hoc )。我强烈建议在其他任何地方使用其中一个自动模拟扩展程序,AutoMoqAutoRhinoMocksAutoFakeItEasyAutoNSubstitute

internal class MyCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customizations.Add(new MySpecimenBuilder());
    }

    private class MySpecimenBuilder : ISpecimenBuilder
    {
        public object Create(object request, ISpecimenContext context)
        {
            var type = request as Type;
            if (type == null || !type.IsInterface)
            {
                return new NoSpecimen(request);
            }

            object specimen = this
                .GetType()
                .GetMethod(
                    "Create",
                    BindingFlags.NonPublic | BindingFlags.Static)
                .MakeGenericMethod(new[] { type })
                .Invoke(this, new object[] { context });

            return specimen;
        }

        private static object Create<TRequest>(ISpecimenContext context)
            where TRequest : class
        {
            var mock = new Mock<TRequest>();
            mock.SetupAllProperties();

            foreach (PropertyInfo propInfo in typeof(TRequest).GetProperties())
            {
                object value = context.Resolve(propInfo.PropertyType);
                propInfo.SetValue(mock.Object, value);
            }
            return mock.Object;
        }
    }
}

答案 1 :(得分:3)

一种简单的可能性是注册工厂方法:

fixture.Register<YourInterface>(() =>  new InterfaceImpl());

让AutoFixture自动为您想要使用其中一个自动模拟自定义的接口创建代理:

  • AutoMoqCustomization
  • AutoRhinoMockCustomization
  • AutoFakeItEasyCustomization

在具体情况下,这将创建一个表示空列表的接口实例 当您希望在该列表中包含测试数据时,您不希望使用自动模拟自定义,而是使用了解IPagination语义的自定义,如in this blog post所述。
如果这太复杂,您仍然可以使用Register方法:

fixture.Register(() => (IPagination<Data>)new Pagination(
                                            fixture.CreateMany<Data>()));