Defining/managing >1 constructor parameters of the same type with independent values when using AutoFixture as a SutFactory
Asked Answered
C

3

9

Using AutoFixture as a SutFactory, I can see that if I register or freeze a value for a type, that value will then be used for all subsequent usages of that type. However, if I have a class with two parameters that are the same type on the constructor such as:

public class ClassA
{
    public double ParameterA { get; private set;}
    public double ParameterB { get; private set;}

    public ClassA(double parameterA, double parameterB)
    {
       ParameterA = parameterA;
       ParameterB = parameterB;
    }

    public void Execute(ClassB object)
    {
        object.Value = (object.Value * ParameterA) /ParameterB;
    }
}

What strategies exist for using autofixture to inject unique pre-defined values for parameterA and parameterB with a view to testing the Calculated value?

*Unfortunately I can't share my exact scenario here, however its using the command pattern to operate on another object, so the only way of setting parameterA and parameterB maintaining the design is to inject them both in, and the constructor is the neatest way to do this in this case.

Compony answered 8/6, 2013 at 8:47 Comment(1)
https://mcmap.net/q/410077/-how-do-i-use-autofixture-v3-with-icustomization-ispecimenbuilder-to-deal-with-constructor-parameter solves the problem, but @Mark Seemann's answer is likely a better roadUel
C
2

Having used the features a bit more, I have personally found that there are not too many objects where this happens, so creating a simple customization for the class is probably best here, e.g.:

public class ClassACustomization: ICustomization
{
    public double ParameterA { get; set;}
    public double ParameterB { get; set;}

    public void Customize(IFixture fixture)
    {
         //Note: these can be initialized how you like, shown here simply for convenience.
         ParameterA = fixture.Create<double>();
         ParameterB = fixture.Create<double>();
         fixture.Customize<ClassA>(c => c
           .FromFactory(() => 
           new ClassA(ParameterA , ParameterB )));
    }
}

I find that this still gives me the flexibility that is great about AutoFixture, without compromising on the ability to inject in specific instances of ParameterA or ParameterB (you could set them up using builder methods, constructor etc... Instead of in the customization to Random values).

Compony answered 13/8, 2013 at 17:29 Comment(0)
L
6

It's my observation that most of the times when I encounter a scenario like that, I don't particularly need to control the values before they're passed to the constructor, but rather that I need to know what the values are.

That's easy to accomplish by adding Structural Inspection Properties to the class:

public class ClassA
{
    public ClassA(double parameterA, double parameterB)
    {
        this.A = parameterA;
        this.B = parameterB;
    }

    public double A { get; private set }

    public double B { get; private set }
}

Now you can ask AutoFixture to create an instance of ClassA without further ado, and subsequently query the instance about the values.

Leucocratic answered 9/6, 2013 at 9:24 Comment(3)
Usually I'd agree with you. In this case (which I didn't make clear in the question), I am thinking more about a situation where I have a calculation that depends on the two values that I would like to verify. I really need to know what the two arguments are to have a reasonable inputs to the calculation that I can verify.Compony
If, for a moment, you consider the case where the dependencies aren't primitives, but interfaces, the exact value shouldn't matter; if it does, the class most likely breaks the Liskov Substitution Principle. From experience, I'm beginning to think that the same sort of principle applies to primitive dependencies. If the value matters, it's probably not a dependency. Perhaps those values are better passed as method arguments? Or perhaps it turns out that they are better modelled as interfaces or classes after all? Can you share your scenario?Leucocratic
I see you're point, and agree generally speaking, I've updated to be a bit more specific above if that helps shed some light on what I'm attempting to achieve.Compony
S
5

One option is to customize the creation algorithm for all instances of ClassA.

var valueA = 1;
var valueB = 2;

var fixture = new Fixture();
fixture.Customize<ClassA>(c => c
    .FromFactory(() => 
        new ClassA(valueA, valueB)));

var result = fixture.Create<ClassA>();
// -> parameterA value is 1
// -> parameterB value is 2
Sunglass answered 8/6, 2013 at 18:11 Comment(9)
Just curious... Any reason you chose to highlight Customize rather than Register ?Uel
No, not any special reason.. Actually, Customize is more explicit.. Both Register and Customize yield the same result though :)Sunglass
@RubenBartelink, its my understanding that register would not solve this scenario, it would simply set both of the double arguments to the same value. In the case above I really need to specify two different parameters that i can control to be injected in. Preferably whilst keeping my tests flexible for future constructor arguments.Compony
@grrrrrrrrrrrrr Don't worry, I understand your question fine (see my link to the related answer on the Q) - I was referring to the fact that one could write Nikos' answer more briefly as fixture.Register( () => new ClassA(valueA,valueB). Customize is more general but Register can in many instances be the perfect tool for the job. (Register's method body consists of fixture.Customize<T>(c => c.FromFactory(creator).OmitAutoProperties());)Uel
@RubenBartelink Thanks that clarifies things. It didn't click that register could be used like that.Compony
@RubenBartelink I guess whichever way its done, in this scenario you need to specify the constructor to use explicitly?Compony
@grrrrrrrrrrrrr BTW If you wanted to specialize only parameterA, the Register syntax can only be fixture.Register((double a)=>new ClassA(a,valueB) or fixture.Register<double,ClassA>(a=>new ClassA(a,valueB) whereas Customize never requires redundantly stating ClassA - i.e., you can do fixture.Customize<ClassA>(c=>c.FromFactory<double>(a=>new ClassA(a,valueB))). Re having to have a ctor invocation expression, you can only avoid it by using my other customization (see link on OP) or the various flavors of same which allow one to pinpoint a specific parameter (only only that)Uel
@RubenBartelink Since using AutoFixture for a while, I feel I have come up with a good compromise solution to this problem (shown by my answer). I thought I'd share this, and mark as accepted as its the most concise answer I've seen to this particular problem. Your answer did lead me to this conclusion. Thanks.Compony
@grrrrrrrrrrrrr Yes, looks fine - as long as your tests are getting cleaner it's all good...Uel
C
2

Having used the features a bit more, I have personally found that there are not too many objects where this happens, so creating a simple customization for the class is probably best here, e.g.:

public class ClassACustomization: ICustomization
{
    public double ParameterA { get; set;}
    public double ParameterB { get; set;}

    public void Customize(IFixture fixture)
    {
         //Note: these can be initialized how you like, shown here simply for convenience.
         ParameterA = fixture.Create<double>();
         ParameterB = fixture.Create<double>();
         fixture.Customize<ClassA>(c => c
           .FromFactory(() => 
           new ClassA(ParameterA , ParameterB )));
    }
}

I find that this still gives me the flexibility that is great about AutoFixture, without compromising on the ability to inject in specific instances of ParameterA or ParameterB (you could set them up using builder methods, constructor etc... Instead of in the customization to Random values).

Compony answered 13/8, 2013 at 17:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.