XUnit 是否在测试 类 之间共享夹具实例?
Does XUnit share fixture instances across test classes?
这是我的一些代码的简化版本:
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);
}
}
两个class都没有标记任何集合属性。他们都属于同一个程序集。
我发现这些测试失败了(但只是偶尔失败),这只能通过 XUnit 在测试 classes 和 TestForNull
[=31 中共享 FixtureData
实例来解释=] 首先(因为它有副作用)。
但是,XUnit documentation 清楚地表明 class 固定装置是 "shared object instance across tests in a single class"。
这是一个错误吗?我应该改变我使用灯具的方式吗?
我正在为 .NET Core 2.3.1 使用 xUnit。
不,您已正确阅读文档。 TL;DR 因为测试 Classes 可以 运行 并行,Class 装置 有 是独立的,这就是它们的全部意义。
如果 xUnit 中存在错误,我会感到惊讶,因为这个 feature/facility 是稳定的并且没有发生变化。
如果你能让你的实际样本失败,那么,是的,这是 xUnit 中的一个错误,但我是说 a) 它现在没有失败 b) 你不会让它失败 c) SELECT 没坏 ;)
希望对您有所帮助 ;)
正如 Ruben Bartelink 在他的回复中所说,"SELECT is not broken",这意味着这是 XUnit 的一个非常核心的功能,一个经过充分验证的测试框架,而且问题出在他们这边的可能性很小。
此外,深入研究 XUnit 代码,这就是它生成 class 固定装置的作用:(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
的调用中可以很容易看出 class 固定装置每次 都会为测试用例重新生成。
那为什么我观察到这种行为?
我在示例中不小心简化了更多我应该拥有的内容。我发现这可能是正在发生的事情的一个更好的例子:
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
实例,单例实际上使它们使用相同的实例。
在我的例子中,我正在独立地查看测试 classes 并且我没有意识到有一个单例,所以我假设问题与测试装置有关(不正确的假设)。而且由于我在提问时遗漏了部分内容,所以有人不可能正确地找出问题所在。
故事寓意:
- 信任经过测试的框架(特别是如果他们专注于测试!)
- Stop overusing singletons
- 在 Whosebug 中提问之前,请确保您已了解问题的所有相关部分
这是我的一些代码的简化版本:
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);
}
}
两个class都没有标记任何集合属性。他们都属于同一个程序集。
我发现这些测试失败了(但只是偶尔失败),这只能通过 XUnit 在测试 classes 和 TestForNull
[=31 中共享 FixtureData
实例来解释=] 首先(因为它有副作用)。
但是,XUnit documentation 清楚地表明 class 固定装置是 "shared object instance across tests in a single class"。
这是一个错误吗?我应该改变我使用灯具的方式吗?
我正在为 .NET Core 2.3.1 使用 xUnit。
不,您已正确阅读文档。 TL;DR 因为测试 Classes 可以 运行 并行,Class 装置 有 是独立的,这就是它们的全部意义。
如果 xUnit 中存在错误,我会感到惊讶,因为这个 feature/facility 是稳定的并且没有发生变化。
如果你能让你的实际样本失败,那么,是的,这是 xUnit 中的一个错误,但我是说 a) 它现在没有失败 b) 你不会让它失败 c) SELECT 没坏 ;)
希望对您有所帮助 ;)
正如 Ruben Bartelink 在他的回复中所说,"SELECT is not broken",这意味着这是 XUnit 的一个非常核心的功能,一个经过充分验证的测试框架,而且问题出在他们这边的可能性很小。
此外,深入研究 XUnit 代码,这就是它生成 class 固定装置的作用:(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
的调用中可以很容易看出 class 固定装置每次 都会为测试用例重新生成。
那为什么我观察到这种行为?
我在示例中不小心简化了更多我应该拥有的内容。我发现这可能是正在发生的事情的一个更好的例子:
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
实例,单例实际上使它们使用相同的实例。
在我的例子中,我正在独立地查看测试 classes 并且我没有意识到有一个单例,所以我假设问题与测试装置有关(不正确的假设)。而且由于我在提问时遗漏了部分内容,所以有人不可能正确地找出问题所在。
故事寓意:
- 信任经过测试的框架(特别是如果他们专注于测试!)
- Stop overusing singletons
- 在 Whosebug 中提问之前,请确保您已了解问题的所有相关部分