代码之家  ›  专栏  ›  技术社区  ›  Alpha

XUnit是否跨测试类共享fixture实例?

  •  1
  • Alpha  · 技术社区  · 6 年前

    这是我所拥有的一些代码的简化版本:

    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 documentation 明确表示类设备是“在 单一的 “班级”。

    这是虫子吗?我应该改变我使用固定装置的方式吗?

    我正在使用XUnit for.NET核心2.3.1。

    2 回复  |  直到 6 年前
        1
  •  1
  •   Ruben Bartelink    6 年前

    不,您已经正确阅读了文档。由于测试类可以并行运行,因此类设备 要独立,这就是他们的全部意义。

    如果Xunit中有一个bug,我会很惊讶,因为这个特性/设施是稳定的,没有发生变化。

    如果你能让你的实际样本失败,那么,是的,这是Xunit的一个错误,但我要说的是a)它现在没有失败b)你不会让它失败c)选择没有失败;)

    希望这有帮助;)

        2
  •  1
  •   Alpha    6 年前

    正如RubenBartelink在他的回答中所说,“选择并没有被破坏”,这意味着这是Xunit的一个非常核心的特性,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 例如,对于每个测试,单例实例实际上使其使用相同的实例。

    在我的例子中,我是独立地查看测试类的,我没有意识到有一个单例,所以我假设问题与测试夹具相关(错误的假设)。因为我问问题时遗漏了一些部分,所以不可能有人正确地发现问题所在。

    故事的寓意:

    • 信任测试过的框架(特别是如果它们专门用于测试!)
    • Stop overusing singletons
    • 在请求stackoverflow之前,请确保您已经了解了问题的所有相关部分。