[英]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.