当我逐个执行测试时,所有测试都可以正常工作,没有问题。但是当我一次性执行它们时,就会遇到问题,如
经过大量的挖掘,我注意到我的测试线程似乎相互干扰:即使测试已经完成,(重复的?)后台调用似乎在另一个测试被执行时继续进行。
涉及的应用程序是从通用应用程序单元测试项目中执行的WinRT组件。下面所看到的所有调用都是异步执行的。为了给我的后台任务足够的时间来完成(如果测试在等待异步后台任务时继续运行,它将在达到测试结束时关闭),我使用主线程休眠适当的时间。
此外,我还在拆卸方法中添加了一个睡眠时间,以确保所有事情都能完成。
为了诊断目前的情况,我查看了测试的执行计划,并且由于它们非常相似,我在代码传递的每个相关位置上都加入了一个
这个涉及到的
在整个层次结构中,使用同一会话对象并通过传递引用来使用所有对象所在位置可用的引用。
当创建数据库时,信号量就发挥作用。在
用于确保在表创建之前不会请求任何数据。通过将其放置在中使用。
在创建最后一张表的地方。
NullReferenceException
和SemaphoreFullException
。经过大量的挖掘,我注意到我的测试线程似乎相互干扰:即使测试已经完成,(重复的?)后台调用似乎在另一个测试被执行时继续进行。
涉及的应用程序是从通用应用程序单元测试项目中执行的WinRT组件。下面所看到的所有调用都是异步执行的。为了给我的后台任务足够的时间来完成(如果测试在等待异步后台任务时继续运行,它将在达到测试结束时关闭),我使用主线程休眠适当的时间。
new ManualResetEvent(false).WaitOne(millisecondsTimeout: 60 * 1000);
此外,我还在拆卸方法中添加了一个睡眠时间,以确保所有事情都能完成。
[TestCleanup]
public void Cleanup()
{
new ManualResetEvent(false).WaitOne(millisecondsTimeout: 60 * 1000);
}
为了诊断目前的情况,我查看了测试的执行计划,并且由于它们非常相似,我在代码传递的每个相关位置上都加入了一个
Debug.WriteLine(session.GetHashCode())
。下面是剥离了mscorlib中的FileNotFoundExceptions
、线程结束消息和符号加载消息的结果。这个涉及到的
Session
对象是由用户创建并传递给Core
,然后由MainApi
和StorageApi
传递。前者将其封装成一个Dispatcher
并将此调度程序传递给UserController
,而StorageApi
仅将会话对象传递给StorageController
,随后传递给DatabaseController
。在整个层次结构中,使用同一会话对象并通过传递引用来使用所有对象所在位置可用的引用。
当创建数据库时,信号量就发挥作用。在
EventBus
中有一个属性。internal static SemaphoreSlim TablesCreatedSemaphore = new SemaphoreSlim(0, 1);
用于确保在表创建之前不会请求任何数据。通过将其放置在中使用。
await EventBus.TablesCreatedSemaphore.WaitAsync();
在执行SuccesfulLogin
事件和请求API数据以及放置数据之间
EventBus.TablesCreatedSemaphore.Release();
在创建最后一张表的地方。
Integration_DownloadAsset_WithInvalidId_ThrowsSomeException
*************************************************
NEW EXECUTION
*************************************************
Core (ctor): 53578018
Main Api (ctor): 53578018
Api dispatcher (ctor): 53578018
UserController (ctor): 53578018
Storage Api (ctor): 53578018
Storage controller (ctor): 53578018
Database controller (ctor): 53578018
User controller (GetApiKeyAsync:pre-call): 53578018
Session (IsLoggedIn): 53578018
User controller (GetApiKeyAsync:pre-successfullogin-event): 53578018
Storage controller (CreateFolderStructure): 53578018
Database Controller(CreateDatabase): 53578018
Session (IsLoggedIn): 53578018
Session (IsLoggedIn): 53578018
Database controller (ctor): 53578018
Integration_DownloadAsset_WhenFileAlreadyExists_IsLocalReturnsTrue
A first chance exception of type 'System.Exception' occurred in mscorlib.dll
Integration_Login_WithValidLogin_RemovesUnusedAssetsFromFolder
A first chance exception of type 'System.Exception' occurred in mscorlib.dll
Integration_DownloadAsset_WithValidId_DownloadsFileToLocalFolder
*************************************************
NEW EXECUTION
*************************************************
Core (ctor): 20039337
Main Api (ctor): 20039337
Api dispatcher (ctor): 20039337
UserController (ctor): 20039337
Storage Api (ctor): 20039337
Storage controller (ctor): 20039337
Database controller (ctor): 20039337
User controller (GetApiKeyAsync:pre-call): 20039337
Session (IsLoggedIn): 20039337
User controller (GetApiKeyAsync:pre-successfullogin-event): 20039337
Storage controller (CreateFolderStructure): 53578018
Storage controller (CreateFolderStructure): 20039337
Database Controller(CreateDatabase): 53578018
Database Controller(CreateDatabase): 20039337
Session (IsLoggedIn): 20039337
Session (IsLoggedIn): 20039337
Database controller (ctor): 20039337
*************************************************
NEW EXECUTION
*************************************************
Core (ctor): 20995649
Main Api (ctor): 20995649
Api dispatcher (ctor): 20995649
UserController (ctor): 20995649
Storage Api (ctor): 20995649
Storage controller (ctor): 20995649
Database controller (ctor): 20995649
User controller (GetApiKeyAsync:pre-call): 20995649
Session (IsLoggedIn): 20995649
User controller (GetApiKeyAsync:pre-successfullogin-event): 20995649
Storage controller (CreateFolderStructure): 53578018
Storage controller (CreateFolderStructure): 20039337
Database Controller(CreateDatabase): 53578018
Database Controller(CreateDatabase): 20039337
Storage controller (CreateFolderStructure): 20995649
Database Controller(CreateDatabase): 20995649
Session (IsLoggedIn): 20995649
A first chance exception of type 'System.Threading.SemaphoreFullException' occurred in mscorlib.dll
A first chance exception of type 'System.Threading.SemaphoreFullException' occurred in mscorlib.dll
笔记
每个测试通过将其名称打印到
Debug.WriteLine
,但显然最后一个没有这样做。具有
Exception
的两个测试是失败的测试,并且很可能是正确的异常。第一个测试仅具有它创建的对象的哈希码,第二个测试具有使用具有先前测试的哈希码的对象调用其方法的内容,而最后一个测试具有对其方法的调用 具有来自前两个测试的哈希码。
这些发现和我的假设与我注意到的其他奇怪行为相一致(有时候
Session
对象在某个地方不会具有身份验证信息,但在其他地方会具有,或者数据会插入数据库两次,尽管主键冲突 - 尽管我不知道为什么这甚至在第一次就是可能的)。我考虑过这可能只是延迟将数据刷新到输出窗口的问题,但它似乎时间太好了。
更改测试的执行顺序仍会导致前两个测试成功,第三个测试失败。每次运行行为都相同:每个测试都使用前一个测试的会话对象执行额外的调用。
示例测试
[TestMethod]
public async Task Integration_DownloadAsset_WithValidId_DownloadsFileToLocalFolder()
{
Debug.WriteLine("Integration_DownloadAsset_WithValidId_DownloadsFileToLocalFolder");
var core = new Core(new Session());
await core.Api.GetApiKeyAsync(new GetApiKeyRequestParameters
{
// Authentication parameters
});
//TODO: why is this necessary?
new ManualResetEvent(false).WaitOne(millisecondsTimeout: 60 * 1000);
await core.Api.DownloadAssetAsync("1891");
// Allow the program to download the file
new ManualResetEvent(false).WaitOne(millisecondsTimeout: 60 * 1000);
var localFolder = await (await (await ApplicationData.Current.LocalFolder.GetFolderAsync("Data")).GetFolderAsync("AccountData")).GetFolderAsync("Files");
var fileNames = (await localFolder.GetFilesAsync()).Select(x => x.Name).ToList();
CollectionAssert.Contains(fileNames, "1891");
}
问题
我认为原因是即使测试已经完成,方法调用仍然存在。这是如何可能的?我该如何防止测试相互交织?
如果这不是原因,那是什么?