AutoFixture создать свойство с внутренним сеттером

Есть ли какой-то способ позволить AutoFixture создавать свойства с помощью внутреннего установщика?

Я посмотрел на источник AutoFixture и обнаружил, что в AutoPropertiesCommand метод GetProperties проверяет, имеет ли свойство GetSetMethod()!= Null. С внутренним установщиком это возвращает нуль, если вы не установите аргумент ignorePublic в true.

Самой простой вещью, конечно, было бы сделать сеттер публичным, но в проекте, над которым я работаю, просто не было бы правильного решения.

Ниже приведен упрощенный фрагмент кода проекта в качестве примера.

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);
    }
}

1 ответ

Решение

В идеале тесты не должны взаимодействовать с internal члены класса, поскольку они явно исключены из его общедоступного API. Вместо этого эти члены будут косвенно проверяться путями кода, инициированными через общедоступный API.

Однако, если это невозможно в вашей конкретной ситуации, возможный обходной путь может заключаться в явном присвоении значения внутренним свойствам из тестов.

Вы можете сделать это одним из двух способов:

  1. Выставив все внутренние элементы в сборке для тестового проекта, используя InternalsVisibleToприписывать.
  2. Представляяизменяемое состояние класса в определенном интерфейсе и реализуя это явно.

В вашем примере вариант 1 будет:

// [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>();
    // ...
}

Вариант 2 вместо этого будет выглядеть примерно так:

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>());
    // ...
}

Вариант 1 довольно быстро реализуется, но имеет побочный эффект открытиявсех внутренних элементов в сборке, что может быть не тем, что вам нужно.
Вариант 2, с другой стороны, позволяет вам контролировать, какая часть состояния объекта должна отображаться как изменяемая, при этом сохраняя его отделенным от собственного публичного API объекта.

В качестве примечания хочу отметить, что, поскольку вы используете xUnit, вы можете воспользоваться поддержкой AutoFixture для теорий данных, чтобы сделать свои тесты несколько более краткими:

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

Если вы предпочитаете установить Name свойство к известному значению, сохраняя при этом остальную часть Dummy объект анонимный, у вас также есть возможность объединить два в одной теории данных:

[Theory, InlineAutoData("SomeName")]
public void Test(string name, Dummy dummy)
{
    ((IModifiableDummy)dummy).SetName(name);
    // ...
}
Другие вопросы по тегам