简体   繁体   English

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

[英]How to let Autofixture create an instance of a type that contains properties with an interface type?

I have such a class: 我有这样一堂课:

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; }
}

A dynamic proxy shall be created around the IPagination interface and the proxy shall be filled with test data. 应在IPagination接口周围创建动态代理,代理应填充测试数据。 Now is it possible to let AutoFixture create an instance of the ViewModel type? 现在可以让AutoFixture创建一个ViewModel类型的实例吗? Be aware that I only know the type at runtime ( typeof(ViewModel) ). 请注意,我只知道运行时的类型( typeof(ViewModel) )。

By now I know I can do this: 到现在为止我知道我可以这样做:

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

Theoretically, it should be possible to fill the properties of an auto-mocked instance. 从理论上讲,应该可以填充自动模拟实例的属性。

Assuming that the IPagination<T> property of the ViewModel type is defined as: 假设ViewModel类型的IPagination<T>属性定义为:

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

We could create an ad-hoc auto-mocking customization, eg MyCustomization . 我们可以创建一个特殊的自动模拟自定义,例如MyCustomization

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

Then, the following call will create an instance of the ViewModel (which is known only at runtime) , provide an auto-mocked instance of the IPagination<Data> and assign values to the properties. 然后,以下调用将创建ViewModel的实例(仅在运行时已知) ,提供IPagination<Data>的自动模拟实例并为属性赋值。

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 MyCustomization

Before you apply this customization, please keep in mind that this should only work for this particular scenario (thus the ad-hoc in the description). 在应用此自定义之前,请记住,这应仅适用于此特定方案(因此说明中的ad-hoc )。 I would strongly suggest to use one of the extensions for Auto Mocking, AutoMoq , AutoRhinoMocks , AutoFakeItEasy , or AutoNSubstitute everywhere else. 我强烈建议在其他地方使用Auto Mocking, 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;
        }
    }
}

A simple possibility is to register a factory method: 一种简单的可能性是注册工厂方法:

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

To let AutoFixture automatically create proxies for interfaces you want to use one of the auto-mocking customizations: 要让AutoFixture自动为您想要使用其中一个自动模拟自定义的接口创建代理:

  • AutoMoqCustomization AutoMoqCustomization
  • AutoRhinoMockCustomization AutoRhinoMockCustomization
  • AutoFakeItEasyCustomization AutoFakeItEasyCustomization

In your concrete case, this will create an instance of the interface representing an empty list. 在具体情况下,这将创建一个表示空列表的接口实例。
When you want to have test data in that list, you don't want to use the auto-mocking customization but a customization that understands the semantics of IPagination as described in this blog post . 如果要在该列表中包含测试数据,则不希望使用自动模拟自定义,而是使用本博客文章中描述的了解IPagination语义的自定义。
If that is too complex, you can still use the Register method: 如果这太复杂,您仍然可以使用Register方法:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM