考虑以下最小重现示例(.NET 7、MSTest 2.2.10):
[TestClass]
public class UnitTest1
{
private AsyncLocal<string> _local = new AsyncLocal<string>();
[TestInitialize]
public async Task Init()
{
_local.Value = "SomeValue";
Console.WriteLine(_local.Value != null); // yields True
await Task.FromResult(0);
Console.WriteLine(_local.Value != null); // yields True
}
[TestMethod]
public void TestMethod1()
{
Console.WriteLine(_local.Value != null); // expected True, actual False
}
[TestCleanup]
public void Cleanup()
{
Console.WriteLine(_local.Value != null); // expected True, actual False
}
}
如果我从测试初始化器中删除行await Task.FromResult(0)
,并将Init声明为public void Init()
,那么一切都按预期工作(所有WriteLines输出True
)。
换句话说:当(且仅当)测试初始化器是async时,测试初始化器中设置的AsyncLocal值会丢失。这对我的用例来说很不方便,因为我正在测试的类内部使用AsyncLocal,因此我不能使用TestInitialize
来初始化它。
我知道我可以通过使测试初始化器同步(并将所有异步操作 Package 在Task.Run(...).Result
中)来解决这个问题。这是预期行为还是我在MSTest中发现了错误?
1条答案
按热度按时间3pmvbmvn1#
AsyncLocal<T>
是为代码作用域的局部变量设计的,也就是说,如果A()
设置了一个AsyncLocal<T>.Value
,然后调用B()
,那么B()
应该会看到那个值。在这种情况下,
TestInitialize
方法不调用TestMethod
方法,因此尝试在TestInitialize
中设置AsyncLocal<T>
是不正确的。相反,应在每个单元测试的arrange部分设置它。如果设置很复杂,则使用helper方法生成值,然后在每个单元测试的arrange部分设置它。如果我从测试初始化器中删除wait Task.FromResult(0)行,并将Init声明为public void Init(),那么一切都按预期工作(所有WriteLines输出True)。
这碰巧奏效了,但并不保证。发生的是这样的:setting an
AsyncLocal<T>
from a synchronous method modifies the logical call context in the closestasync
method further up the call stack(更多细节在我的博客上)。我建议不要依赖这种行为,因为它经常令人惊讶。这是预期行为还是我在MSTest中发现了错误?
这是预期行为,具体来说,
AsyncLocal<T>
的预期行为;这和微软科技测试没有任何关系。