Setting property value on child instance to a fixed value with Autofixture
Asked Answered
Y

3

31

Is it possible to assign a fixed value to a property on a child instance when building a parent with Autofixture? It will add default values to all the properties on the child instance like a charm, but I would like to override and assign a specific value to one of the properties on the child instance.

Given this parent/child relationship:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public Address Address { get; set; }
}

public class Address
{
    public string Street { get; set; }
    public int Number { get; set; }
    public string City { get; set; }
    public string PostalCode { get; set; }
}

I would like to assign a specific value to the City property on the address instance. I was thinking in the lines of this test code:

var fixture = new Fixture();

var expectedCity = "foo";

var person = fixture
    .Build<Person>()
    .With(x => x.Address.City, expectedCity)
    .Create();

Assert.AreEqual(expectedCity, person.Address.City);

That is not possible. I guess, by the reflection exception

System.Reflection.TargetException : Object does not match target type.

...that Autofixture tries to assign the value to a City property on the Person instance instead of an Address instance.

Any suggestions?

And yes, I know that I could just add an extra step like the following:

var fixture = new Fixture();

var expectedCity = "foo";

// extra step begin
var address = fixture
    .Build<Address>()
    .With(x => x.City, expectedCity)
    .Create();
// extra step end

var person = fixture
    .Build<Person>()
    .With(x => x.Address, address)
    .Create();

Assert.AreEqual(expectedCity, person.Address.City);

...but was hoping for the first version or something similar (fewer steps, more terse).

Note: I'm using Autofixture v3.22.0

Yama answered 7/1, 2015 at 8:33 Comment(1)
H
12

Not to be dismissive of the question, but the simplest solution might actually be this:

[Fact]
public void SimplestThingThatCouldPossiblyWork()
{
    var fixture = new Fixture();
    var expectedCity = "foo";
    var person = fixture.Create<Person>();
    person.Address.City = expectedCity;
    Assert.Equal(expectedCity, person.Address.City);
}

Assignment of explicit values to properties is something most languages already excel at (C# certainly does), so I don't think AutoFixture needs a complicated DSL to reproduce half of that functionality.

Hyperplane answered 7/1, 2015 at 9:7 Comment(6)
While it is obvious to most of us that assignment of explicit values to properties in C# is (almost) without effort, the original question was can I in a parent/child relationship assign a fixed value to a property of the child with Autofixture. The reason for the question is merely on the aesthetics as I think that grouping the value assignments using .With(….) syntax would be “prettier” when having multiple assignments (as subjectively as that is). Aesthetics aside, your answer gets the job done.Yama
You can fix a value by implementing a custom ISpecimenBuilder, but not (I think) with the fluent DSL, when it's a Train Wreck property. I did get the impression from the question that you were aiming for pretty, but if you want the convention-based approach, I'll be happy to share it.Hyperplane
No, but thanks anyway. The train wreck is parked at the outermost boundary of my application and simply models an incoming data structure, so I’ll live with a handful of test cases that look...well...less pretty ;-)Yama
I came across this question as I have the same issue. In my opinion, one of the nice features of AF is the ability to use the fluent API. It surprises me that AF does not support this. Since I am able to write the code fluently, it should work. To me it is a case of "Least Astonishment".Vicechairman
Sadly this is not an option for me. If you have validation inside your constructor, this won't work.Tmesis
Public setters is not a thing in 2020Fulltime
C
31

For completeness, here's another way to do it:

fixture.Customize<Address>(c => 
    c.With(addr => addr.City, "foo"));

var person = fixture.Create<Person>();

This will customize the creation of all instances of Address

If you end up using this often enough, it may be worthwhile wrapping it inside an ICustomization:

public class AddressConventions : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customize<Address>(c => 
            c.With(addr => addr.City, "foo"));
    }
}

fixture.Customize(new AddressConventions());
Coretta answered 15/1, 2015 at 15:23 Comment(0)
H
12

Not to be dismissive of the question, but the simplest solution might actually be this:

[Fact]
public void SimplestThingThatCouldPossiblyWork()
{
    var fixture = new Fixture();
    var expectedCity = "foo";
    var person = fixture.Create<Person>();
    person.Address.City = expectedCity;
    Assert.Equal(expectedCity, person.Address.City);
}

Assignment of explicit values to properties is something most languages already excel at (C# certainly does), so I don't think AutoFixture needs a complicated DSL to reproduce half of that functionality.

Hyperplane answered 7/1, 2015 at 9:7 Comment(6)
While it is obvious to most of us that assignment of explicit values to properties in C# is (almost) without effort, the original question was can I in a parent/child relationship assign a fixed value to a property of the child with Autofixture. The reason for the question is merely on the aesthetics as I think that grouping the value assignments using .With(….) syntax would be “prettier” when having multiple assignments (as subjectively as that is). Aesthetics aside, your answer gets the job done.Yama
You can fix a value by implementing a custom ISpecimenBuilder, but not (I think) with the fluent DSL, when it's a Train Wreck property. I did get the impression from the question that you were aiming for pretty, but if you want the convention-based approach, I'll be happy to share it.Hyperplane
No, but thanks anyway. The train wreck is parked at the outermost boundary of my application and simply models an incoming data structure, so I’ll live with a handful of test cases that look...well...less pretty ;-)Yama
I came across this question as I have the same issue. In my opinion, one of the nice features of AF is the ability to use the fluent API. It surprises me that AF does not support this. Since I am able to write the code fluently, it should work. To me it is a case of "Least Astonishment".Vicechairman
Sadly this is not an option for me. If you have validation inside your constructor, this won't work.Tmesis
Public setters is not a thing in 2020Fulltime
C
-1

You can use a Do method on the builder:

var person = this.fixture
               .Build<Person>()
               .Do( x => x.Address.City = expectedCity )
               .Create();

You can also inject and freeze your instance to the fixture:

this.fixture.Inject( expectedCity );
Chigoe answered 7/1, 2015 at 8:39 Comment(5)
Have you actually tried to run that code? It throws an NullReferenceException when I run it.Yama
...and Inject will set all the string properties to the value of expectedCity - not really what I wantYama
Indeed, there is a NullReferenceException for the Do method, what IMO can be considered as bug, because this method comes from interface named IPostprocessComposer<T>, so post-processing should be done after the object and it's dependencies are created.Chigoe
The simplest solution is just the assign statement after object is created: person.Address.City = expectedCity;Chigoe
Look like you are right and it is a bug. According to its designer blog (blog.ploeh.dk/2009/06/09/…), "you can mix With and Do, and ObjectBuilder<T> will preserve the ordering."Nestle

© 2022 - 2024 — McMap. All rights reserved.