XUnit是否在测试类之间共享fixture实例



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

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文档清楚地表明,类fixture是"在单个类中的测试之间共享对象实例"。

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

我将xUnit用于.NET Core 2.3.1。

正如Ruben Bartelink在回应中所说,"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的调用中可以很容易地看出,每次为测试用例重新生成类fixture。

那为什么我观察到这种行为呢

我不小心在示例中简化了本应简化的内容。我发现这可能是正在发生的事情的一个更好的例子:

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实例,singleton实际上也会使其使用相同的实例。

在我的案例中,我独立地查看了测试类,但我没有意识到存在单例,所以我认为问题与测试固定装置有关(错误的假设)。由于我在提问时遗漏了一些部分,所以不可能有人正确地发现问题所在。

故事寓意:

  • 信任经过测试的框架(特别是那些专门从事测试的框架!)
  • 停止过度使用背心
  • 在StackOverflow中询问之前,请确保您已经了解了问题的所有相关部分

不,您已经正确阅读了文档。TL;DR因为测试类可以并行运行,所以类固定装置具有独立的,这就是它们的全部意义。

如果xUnit中有一个错误,我会感到惊讶,因为这个功能/设施是稳定的,没有发生变化。

如果你能让你的实际样本失败,那么,是的,这是xUnit中的一个错误,但我想说的是a)它现在没有失败b)你不会让它失败c)SELECT没有被破坏;)

希望这能有所帮助;)

最新更新