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
Julian
  • 33,915
  • 22
  • 119
  • 174
iam3yal
  • 2,188
  • 4
  • 35
  • 44

2 Answers2

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)))));
iam3yal
  • 2,188
  • 4
  • 35
  • 44
  • 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! – julien nascimento Dec 18 '19 at 20:46
  • 1
    @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;` – iam3yal Dec 20 '19 at 07:05
  • 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! – julien nascimento Dec 20 '19 at 16:15
3

One possible way:

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

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

fixture.Inject(segmentHandler);
dcastro
  • 66,540
  • 21
  • 145
  • 155
  • 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. – iam3yal Sep 16 '14 at 15:43
  • @eyalalonn you could just replace `parameterHandler` in the example with your custom object. – Pyce Oct 28 '22 at 15:41