Select specific constructor with AutoFixture
Asked Answered
A

2

7

I'm using AutoFixture and I'd like to use a specific constructor.

I have the following code and I like to select the constructor with ITemplateParameterHandler.

public sealed class TemplateSegmentHandler : ITemplateSegmentHandler
{
    public TemplateSegmentHandler(ITemplateIterator iterator)
        : this(new TemplateParameterHandler(iterator))
    {
        Contract.Requires(iterator != null);
    }

    public TemplateSegmentHandler(ITemplateParameterHandler parameterHandler)
    {
        Contract.Requires(parameterHandler != null);

        _parameterHandler = parameterHandler;

        _parameterHandler.Ending += EndingParameter;
    }

    // ...
}

EDIT:

I want to inject the following fake implementation. (I'm using NSubstitute to create the fake object.)

public sealed class CustomTemplateParameter : ICustomization
{
    private readonly ITemplateParameterHandler _context;

    public CustomTemplateParameter()
    {
        _context = Substitute.For<ITemplateParameterHandler>();
    }

    public void Customize(IFixture fixture)
    {
        fixture.Customize<ITemplateParameterHandler>(c => c.FromFactory(() => _context));
    }

    public CustomTemplateParameter SetCatchAll(bool isCatchAll)
    {
        _context.IsCatchAll.Returns(isCatchAll);

        return this;
    }
}

Here is the way I'm trying to use it.

[Fact]
public void Should_return_true_when_the_segment_has_a_catch_all_parameter()
{
    TemplateSegmentHandler segmentHandler = new Fixture().Customize(new TemplateSegmentHandlerFixture())
                                                         .Customize(new CustomTemplateParameterHandler()
                                                                        .SetCatchAll(true))
                                                         .Create<TemplateSegmentHandler>();

    segmentHandler.Parameter.Start();
    segmentHandler.Parameter.End();

    segmentHandler.HasCatchAll.Should().BeTrue();
}

But it still selects the wrong constructor and I'm getting the following error.

Ploeh.AutoFixture.ObjectCreationExceptionAutoFixture was unable to create an instance from Somia.Web.Routing.Template.ITemplateIterator, most likely because it has no public constructor, is an abstract or non-public type.

Request path:
      Somia.Web.Routing.Template.TemplateSegmentHandler --> 
       Somia.Web.Routing.Template.ITemplateIterator iterator --> 
        Somia.Web.Routing.Template.ITemplateIterator
Anglican answered 16/9, 2014 at 13:52 Comment(0)
A
4

I ended up implementing IMethodQuery and select the ctor I wanted.

public sealed class SelectedFirstConstructorQuery : IMethodQuery
{
    private readonly Type _type;

    public SelectedFirstConstructorQuery(Type type)
    {
        _type = type;
    }

    public IEnumerable<IMethod> SelectMethods(Type type)
    {
        if (type == null)
        {
            throw new ArgumentNullException("type");
        }

        return from ci in type.GetConstructors()
               let parameter = ci.GetParameters().First()
               where parameter.ParameterType == _type
               select new ConstructorMethod(ci) as IMethod;
    }
}

Usage:

fixture.Customize<TemplateSegmentHandler>(c => c.FromFactory(new MethodInvoker(new SelectedFirstConstructorQuery(typeof(ITemplateParameterHandler)))));
Anglican answered 16/9, 2014 at 17:19 Comment(3)
Hi, thank you for your reply. So, using this SelectedFirstConstructorQuery, only the first Constructor will have been used, right? But, if I could use a second constructor in the same class How Can I do this? Thanks!Gregorygregrory
@juliennascimento I'm pretty sure you have to create a new issue about it as I addressed the original problem I had but if I understood you correctly what you need to do is have the constructor take two types and then change the LINQ query to something like this return from ci in type.GetConstructors() let param1 = ci.GetParameters().First() let param2 = ci.GetParameters().ElementAtOrDefault(2) where param1.ParameterType == _type1 && param2?.ParameterType == _type2 select new ConstructorMethod(ci) as IMethod;Anglican
Hi. I'm writing a test where I have two different constructors so I need to test it. But, in other situations, some classes have 3 constructors ( all Immutable classes) therefore I have this question. I'll try to do using your tips, thanks!Gregorygregrory
D
4

One possible way:

var parameterHandler = fixture.Create<ITemplateParameterHandler>();

var segmentHandler = fixture.Build<TemplateSegmentHandler>()
                            .FromFactory(() => new TemplateSegmentHandler(parameterHandler));

fixture.Inject(segmentHandler);
Dhar answered 16/9, 2014 at 14:38 Comment(2)
Yeah, sorry that I didn't wrote it in my OP but I want to inject a custom object, I'll update my post with more details.Anglican
@eyalalonn you could just replace parameterHandler in the example with your custom object.Tarsal
A
4

I ended up implementing IMethodQuery and select the ctor I wanted.

public sealed class SelectedFirstConstructorQuery : IMethodQuery
{
    private readonly Type _type;

    public SelectedFirstConstructorQuery(Type type)
    {
        _type = type;
    }

    public IEnumerable<IMethod> SelectMethods(Type type)
    {
        if (type == null)
        {
            throw new ArgumentNullException("type");
        }

        return from ci in type.GetConstructors()
               let parameter = ci.GetParameters().First()
               where parameter.ParameterType == _type
               select new ConstructorMethod(ci) as IMethod;
    }
}

Usage:

fixture.Customize<TemplateSegmentHandler>(c => c.FromFactory(new MethodInvoker(new SelectedFirstConstructorQuery(typeof(ITemplateParameterHandler)))));
Anglican answered 16/9, 2014 at 17:19 Comment(3)
Hi, thank you for your reply. So, using this SelectedFirstConstructorQuery, only the first Constructor will have been used, right? But, if I could use a second constructor in the same class How Can I do this? Thanks!Gregorygregrory
@juliennascimento I'm pretty sure you have to create a new issue about it as I addressed the original problem I had but if I understood you correctly what you need to do is have the constructor take two types and then change the LINQ query to something like this return from ci in type.GetConstructors() let param1 = ci.GetParameters().First() let param2 = ci.GetParameters().ElementAtOrDefault(2) where param1.ParameterType == _type1 && param2?.ParameterType == _type2 select new ConstructorMethod(ci) as IMethod;Anglican
Hi. I'm writing a test where I have two different constructors so I need to test it. But, in other situations, some classes have 3 constructors ( all Immutable classes) therefore I have this question. I'll try to do using your tips, thanks!Gregorygregrory

© 2022 - 2024 — McMap. All rights reserved.