AutoFixture create property with internal setter
Asked Answered
A

1

7

Is there some kind of way to let AutoFixture create properties with an internal setter?

I've looked at the AutoFixture source and found that in the AutoPropertiesCommand the GetProperties method checks whether a property has GetSetMethod() != null. With an internal setter this returns null, unless you set the ignorePublic argument to true.

The easiest thing would of course be to make the setter public but in the project I'm working on this just wouldn't be the right solution.

Below is a simplified piece of code from the project as an example.

public class Dummy
{
    public int Id { get; set; }
    public string Name { get; internal set; }
}

public class TestClass
{
    [Fact]
    public void Test()
    {
        var dummy = new Fixture().Create<Dummy>();
        Assert.NotNull(dummy.Name);
    }
}
Airboat answered 13/10, 2013 at 12:45 Comment(1)
Why wouldn't it be the right solution to make the setter public?Bioscope
D
5

Ideally, the tests shouldn't have to interact with the internal members of a class, since they are explicitly excluded from its public API. Instead, these members would be tested indirectly by the code paths initiated through the public API.

However, if this isn't feasible in your particular situation, a possible workaround could be to explicitly assign a value to the internal properties from within the tests.

You can do that in one of two ways:

  1. By exposing all internal members within the assembly to the test project using the InternalsVisibleTo attribute.
  2. By representing the modifiable state of the class in a specific interface and implement that explicitly.

In your example, option 1 would be:

// [assembly:InternalsVisibleTo("Tests")]
// is applied to the assembly that contains the 'Dummy' type

[Fact]
public void Test()
{
    var fixture = new Fixture();
    var dummy = fixture.Create<Dummy>();
    dummy.Name = fixture.Create<string>();
    // ...
}

Option 2, instead, would be something like:

public class Dummy : IModifiableDummy
{
    public string Name { get; private set; }

    public void IModifiableDummy.SetName(string value)
    {
        this.Name = value;
    }
}

[Fact]
public void Test()
{
    var fixture = new Fixture();
    var dummy = fixture.Create<Dummy>();
    ((IModifiableDummy)dummy).SetName(fixture.Create<string>());
    // ...
}

Option 1 is fairly quick to implement, but has the side effect of opening up all internal members within the assembly, which may not be what you want.
Option 2, on the other hand, allows you to control what part of the object's state should be exposed as modifiable, while still keeping it separated the object's own public API.

As a side note, I'd like to point out that, since you're using xUnit, you can take advantage of AutoFixture's support for Data Theories to make your tests slightly more terse:

[Theory, AutoData]
public void Test(Dummy dummy, string name)
{
    ((IModifiableDummy)dummy).SetName(name);
    // ...
}

If you prefer to set the Name property to a known value while still keeping the rest of the Dummy object anonymous, you have also the possibility to combine the two within the same Data Theory:

[Theory, InlineAutoData("SomeName")]
public void Test(string name, Dummy dummy)
{
    ((IModifiableDummy)dummy).SetName(name);
    // ...
}
Demoss answered 14/10, 2013 at 21:50 Comment(1)
Thanks for your answer. I've solved it by adding a custom ISpecimenBuilder that creates an instance of Dummy and then loops through all properties and creates a fixture for each of them. This is pretty much the same as the AutoPropertiesCommand is doing as well.Airboat

© 2022 - 2024 — McMap. All rights reserved.