[英]When multiple unit tests copy the same file, running all unit tests fail
描述
我正在為一個方法編寫單元測試,該方法將文件從源復制到目標。 基本上它包括這個代碼:
public void MyMethod()
{
// ...
File.Copy(source, destination, true);
// ...
}
在我的單元測試項目中,我有一個測試文件:( test.png
),它位於我的單元測試項目的Resources
文件夾中。 我將Copy to Output
屬性設置為Always
。
我有3個單元測試正在測試這個方法。
當他們點擊復制文件的代碼行時: source = "Resources\\\\test.png"
。
問題
當我單獨進行單元測試時,它們都通過了,一切都很好。 但是,當我在Visual Studio中運行所有測試時,我得到此運行時錯誤並且單元測試失敗:
System.IO.DirectoryNotFoundException
找不到路徑'Resources \\ test.png'的一部分。
我的想法......(更新)
可能是因為Visual Studio在一個單獨的線程中同時運行每個單元測試,並且它們都同時訪問同一個文件?
我認為對於每個單元測試,Visual Studio都是清理bin/Debug
和bin/Release
文件夾。 然后,它復制該文件夾中的所有必需項目文件。 這有時導致文件實際上不存在?
題
我該如何解決這個問題?
是否有任何配置設置來解決此問題?
當多個單元測試訪問同一個文件時,如何在Visual Studio(和Team City)中運行所有單元測試?
你可以嘗試按照從指令排除多線程問題MSDN:多CPU /核機器上並行執行單元測試 ,設置parallelTestCount
至1
。 如果測試現在通過,你就縮小了問題范圍。
但是,如果在組中運行它們時測試仍然失敗 - 我認為這是更可能的情況 - 那么我的建議是檢查這些測試共享的任何狀態。 您描述的模式(即隔離傳遞;不隔離時失敗)是通常由(錯誤地)共享狀態的測試所表現出的症狀,並且這些測試正在修改該狀態,導致一個或多個測試失敗。
訪問同一個文件應該不是問題。 確保沒有cleanUp Fixture(TestSuite級別)來刪除文件。 因為從例外看起來該文件在運行測試后被刪除。
並發讀操作也很好,完全合法。 如果您的單元測試覆蓋了文件,那么這是一個問題。
發生了什么事,因為我使用測試文件的相對路徑,出於某種原因,在批量運行單元測試時,測試運行器工作目錄與運行單個測試時不同,因此無法找到目錄。
所以我使用這個函數來構建測試文件的絕對路徑:
private string GetFilePath([CallerFilePath] string path = "")
{
return path;
}
然后:
string projectDir = Path.GetDirectoryName(GetFilePath());
string testFile = Path.Combine(projectDir, @"Resources\test.png";
我推測你的問題是,在給定明確的“找不到目錄”異常的情況下,其中一個經過測試的方法會更改目錄。 文件鎖定或任何並發問題都不可能導致所描述的行為。
如果單元測試你不應該真正測試File.Copy(或任何File類方法)是否有效,因為你沒有編寫該代碼。 相反,您應該測試您的代碼是否與文件類型正確交互(即,當您調用“復制”時,它是否傳遞了正確的源文件名,desination文件名和覆蓋值)。 首先為File類創建一個接口,為它實現接口的包裝器;
public interface IFileWrapper
{
void Copy(string sourceFileName,string destFileName,bool overwrite);
//Other required file system methods and properties here...
}
public class FileWrapper : IFileWrapper
{
public void Copy(string sourceFileName, string destFileName, bool overwrite)
{
File.Copy(sourceFileName, destFileName, overwrite);
}
}
然后,您應該使您正在測試的類包含IFileWrapper參數(依賴注入) 。 在單元測試中,您可以使用模擬框架(如Moq)或者您可以編寫自己的模擬;
public class MockFileWrapper : IFileWrapper
{
public string SoureFileName { get; set; }
public string DestFileName { get; set; }
public bool Overwrite { get; set; }
public void Copy(string sourceFileName, string destFileName, bool overwrite)
{
SoureFileName = sourceFileName;
DestFileName = destFileName;
Overwrite = overwrite;
}
}
在實際實現中,將FileWrapper作為IFileWrapper參數傳遞,但在單元測試中傳遞MockFileWrapper。 通過在單元測試中檢查mockFileWrapper的屬性,您現在可以確定是否類調用Copy以及如何調用它。 由於您不再在單元測試之間共享實際文件,因此可以避免測試共享狀態或可能鎖定文件的可能性。
正如您在答案中提到的,測試框架並不總是在將工作目錄設置為構建輸出文件夾的情況下運行測試。
要指示測試框架將構建輸出中的構建工件或其他文件放入測試目錄,您需要使用DeploymentItemAttribute
。 對於您的情況,您可以執行以下操作:
const string destination = "Destination.txt";
const string source = "MyData.txt";
[DeploymentItem(source)]
[TestMethod]
public void MyMethod()
{
// …
File.Copy(source, destination, true);
// …
}
[TestCleanup]
public void Cleanup()
{
// Clean up the destination so that subsequent tests using
// the same deploy don’t collide.
File.Delete(destination);
}
還要確保您的文件標記為內容構建操作和始終復制。 否則,它們將不在構建輸出目錄中,並且將無法復制到測試目錄。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.