Search code examples
c#autofixturereadonly-attribute

Autofixture and read only properties


Let's consider two version (one with read only properties) of the same very simple entity:

public class Client
{
    public Guid Id { get; set; }

    public string Name { get; set; }
}

vs

public class Client
{
    public Client(Guid id, string name)
    {
        this.Id = id;
        this.Name = name;
    }

    public Guid Id { get; }

    public string Name { get; }
}

When I try to use Autofixture, it will work correctly and as expected with both of them. The problems start, when I try to predefine one of the parameters using .with() method:

var obj = this.fixture.Build<Client>().With(c => c.Name, "TEST").Build();

This will throw error

System.ArgumentException: The property "Name" is read-only.

But it seems that Autofixture knows how to use constructors! And it seems that actual Build<>() method creates an instance of an object not Create()! If build would just prepare builder, with would setup properties, and then Create would instantiate object it would work properly with read only properties.

So why was this (misleading) strategy used here? I've found an answer here that states it's to amplify feedback with tests, but I don't see the usefulness to use FromFactory() especially when a list of parameters is extensive. Wouldn't moving object instantiation from Build() method to Create() method be more intuitive?


Solution

  • AutoFixture is, indeed, capable of creating constructor arguments, and invoke constructors. How to control a particular constructor argument is a FAQ, so if that had been the only question, I'd had closed it as a duplicate of Easy way to specify the value of a single constructor parameter?

    This post, however, also asks about the design choice behind the behaviour of the Build API, and I will answer that here.

    In the second example, Name is a read-only property, and you can't change the value of a read-only property. That's part of .NET (and most other languages) and not a design choice of AutoFixture.

    Let's be absolutely clear on this: Name is a property. Technically, it has nothing to do with the class' constructor.

    I assume that you consider Name to be associated with the constructor's name argument, because one exposes the other, but we only know that because we have the source code. There's no technically safe way for an external observer to be sure that these two are connected. An outside observer, such as AutoFixture, could attempt to guess that such a connection exists, but there are no guarantees.

    It's technically possible to write code like this:

    public class Person
    {
        public Person(string firstName, string lastName)
        {
            this.FirstName = lastName;
            this.LastName = firstName;
        }
    
        public string FirstName { get; }
    
        public string LastName { get; }
    }
    

    This compiles just fine, even though the values are switched around. AutoFixture would be unable to detect issues like that.

    It might be possible to give AutoFixture a heuristic where the Build API attempts to guess 'what you mean' when you refer to a read-only property, but back when I was still the benevolent dictator of the project, I considered that to be a feature with unwarranted complexity. It's possible that the new maintainers may look differently on the topic.

    As a general observation, I consider the entire Build API a mistake. In the last many years I wrote tests with AutoFixture, I never used that API. If I still ran the project today, I'd deprecate that API because it leads people into using AutoFixture in a brittle way.

    So this is very much an explicit design choice.