Разделяет ли XUnit экземпляры приборов между тестовыми классами? - PullRequest
0 голосов
/ 20 декабря 2018

Это упрощенная версия кода, который у меня есть:

public class FixtureData
{
    public object SomeValue { get; set; }
}

public class TestForNull : IClassFixture<FixtureData>
{
    private readonly FixtureData _data;

    public TestForNull(FixtureData data)
    {
        _data = data;
    }

    [Fact]
    public void TestForNull()
    {
        _data.SomeValue = null;
        Assert.Null(_data.SomeValue);
    }
}

public class TestForObject : IClassFixture<FixtureData>
{
    private readonly FixtureData _data;

    public TestForObject(FixtureData data)
    {
        _data = data;
    }

    [Fact]
    public void TestForObject()
    {
        Assert.NotNull(_data.SomeValue);
    }
}

Оба класса не помечены каким-либо атрибутом коллекции.Они оба принадлежат одной и той же сборке.

Я вижу, что эти тесты не пройдены (но только спорадически), что можно объяснить только тем, что XUnit разделяет экземпляр FixtureData между тестовыми классами, а TestForNull выполняется первым (поскольку он имеет побочные эффекты).

Тем не менее, документация XUnit разъясняет, что фиксации класса предназначены для "экземпляра общего объекта в тестах в одном классе".

Это ошибка?Должен ли я изменить способ использования приборов?

Я использую xUnit для .NET Core 2.3.1.

Ответы [ 2 ]

0 голосов
/ 21 декабря 2018

Как сказал Рубен Бартелинк в своем ответе, «SELECT не сломан», это означает, что это очень важная особенность XUnit, хорошо зарекомендовавшей себя тестовой среды, и очень маловероятно, что проблема на его стороне.

Кроме того, копаясь в коде XUnit, это то, что он делает для генерации фикстур классов: ( Source )

var createClassFixtureAsyncTasks = new List<Task>();
foreach (var interfaceType in testClassTypeInfo.ImplementedInterfaces.Where(i => i.GetTypeInfo().IsGenericType && i.GetGenericTypeDefinition() == typeof(IClassFixture<>)))
    createClassFixtureAsyncTasks.Add(CreateClassFixtureAsync(interfaceType.GetTypeInfo().GenericTypeArguments.Single()));

if (TestClass.TestCollection.CollectionDefinition != null)
{
    var declarationType = ((IReflectionTypeInfo)TestClass.TestCollection.CollectionDefinition).Type;
    foreach (var interfaceType in declarationType.GetTypeInfo().ImplementedInterfaces.Where(i => i.GetTypeInfo().IsGenericType && i.GetGenericTypeDefinition() == typeof(IClassFixture<>)))
        createClassFixtureAsyncTasks.Add(CreateClassFixtureAsync(interfaceType.GetTypeInfo().GenericTypeArguments.Single()));
}

await Task.WhenAll(createClassFixtureAsyncTasks);

Из вызовов к CreateClassFixtureAsync легко сказать, чтоклассовые приспособления регенерируются каждый раз для тестового примера.

Тогда почему поведение, которое я наблюдал?

Я случайно упростил в своем примере больше, что я должен иметь.Я обнаружил, что это может быть лучшим примером того, что происходит:

public class FixtureData
{
    public object SomeValue => HiddenSingleton.Instance.SomeValue;
}

public class HiddenSingleton
{
    private static HiddenSingleton _instance;
    public static HiddenSingleton Instance
    {
        get
        {
            if (_instance != null) return _instance;

            _instance = new HiddenSingleton();
            return _instance;
        }
    }

    public object SomeValue { get; set; }
}

public class TestForNull : IClassFixture<FixtureData>
{
    private readonly FixtureData _data;

    public TestForNull(FixtureData data)
    {
        _data = data;
    }

    [Fact]
    public void TestForNull()
    {
        _data.SomeValue = null;
        Assert.Null(_data.SomeValue);
    }
}

public class TestForObject : IClassFixture<FixtureData>
{
    private readonly FixtureData _data;

    public TestForObject(FixtureData data)
    {
        _data = data;
    }

    [Fact]
    public void TestForObject()
    {
        Assert.NotNull(_data.SomeValue);
    }
}

В этом случае, если посмотреть на него напрямую, это совершенно очевидно: даже если XUnit генерирует независимый экземпляр FixtureData для каждогоtest, singleton фактически делает так, чтобы они использовали один и тот же экземпляр.

В моем случае я смотрел на тестовые классы независимо и не осознавал, что существует singleton, поэтому я предположил, что проблема связана сс тестовыми приборами (неверное предположение).И из-за частей, которые я пропустил, задавая вопрос, для кого-то было невозможно правильно понять, что было неправильно.

Мораль истории:

0 голосов
/ 21 декабря 2018

Нет, вы правильно прочитали документы.TL; DR, потому что тестовые классы могут работать параллельно, классовые приспособления имеют независимость , в этом их суть.

Я был бы удивлен, если в xUnit возникла ошибкаэто потому, что эта функция / средство стабильно и не претерпевает изменений.

Если вы можете заставить свой фактический пример не сработать, тогда да, это ошибка в xUnit, но я говорю: а) это не сбой сейчас б) выне собираемся заставить его потерпеть неудачу в) SELECT не сломан;)

Надеюсь, это поможет;)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...