How to let Autofixture create an instance of a type that contains properties with an interface type?
Asked Answered
R

2

9

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. Now is it possible to let AutoFixture create an instance of the ViewModel type? Be aware that I only know the type at runtime (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))
Rocketry answered 18/10, 2012 at 7:33 Comment(2)
What does the view model and the answer from your last question have to do with the last sentence that actually is the question? Please keep your question focused.Paries
You are right I have so many questions in mind :)Rocketry
M
4

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:

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

We could create an ad-hoc auto-mocking customization, e.g. 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.

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

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). I would strongly suggest to use one of the extensions for Auto Mocking, AutoMoq, AutoRhinoMocks, AutoFakeItEasy, or AutoNSubstitute everywhere else.

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;
        }
    }
}
Mawkin answered 18/10, 2012 at 15:31 Comment(3)
Wouldn't it be a tad easier to invoke mock.SetupAllProperties()?Blum
Just made the implementation even more simple.Mawkin
Here is another approach describing how to configure AutoFixture.AutoMoq to set up all mock instances as well as automatically populate their properties.Mawkin
P
5

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:

  • AutoMoqCustomization
  • AutoRhinoMockCustomization
  • 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.
If that is too complex, you can still use the Register method:

fixture.Register(() => (IPagination<Data>)new Pagination(
                                            fixture.CreateMany<Data>()));
Paries answered 18/10, 2012 at 7:49 Comment(2)
Would it be also possible to let Autofixture create a dynamic proxy around an interface and fill it with test data?Rocketry
Is it possible to fill the created proxy list with data?Rocketry
M
4

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:

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

We could create an ad-hoc auto-mocking customization, e.g. 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.

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

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). I would strongly suggest to use one of the extensions for Auto Mocking, AutoMoq, AutoRhinoMocks, AutoFakeItEasy, or AutoNSubstitute everywhere else.

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;
        }
    }
}
Mawkin answered 18/10, 2012 at 15:31 Comment(3)
Wouldn't it be a tad easier to invoke mock.SetupAllProperties()?Blum
Just made the implementation even more simple.Mawkin
Here is another approach describing how to configure AutoFixture.AutoMoq to set up all mock instances as well as automatically populate their properties.Mawkin

© 2022 - 2024 — McMap. All rights reserved.