当多个单元测试复制同一文件时,运行所有单元测试失败

时间:2016-05-20 20:39:20

标签: c# visual-studio unit-testing mstest teamcity-9.0

描述

我正在为一个方法编写单元测试,该方法将文件从源复制到目标。基本上它包含以下代码:

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/Debugbin/Release个文件夹。然后,它复制该文件夹中的所有必需项目文件。这会导致文件实际上不存在吗?

问题

如何解决此问题?

是否有任何配置设置可以解决此问题?

当多个单元测试访问同一个文件时,如何在Visual Studio(和Team City)中运行所有单元测试?

6 个答案:

答案 0 :(得分:3)

您可以尝试按照MSDN: Executing Unit Tests in parallel on a multi-CPU/core machine中的说明排除多线程问题,并将parallelTestCount设置为1。如果测试现在通过,你就缩小了问题范围。

但是,如果您在组中运行它们时测试仍然失败 - 我认为这是更可能的情况 - 那么我的建议是检查这些测试共享的任何状态。您描述的模式(即隔离传递;不隔离时失败)是通常由(错误地)共享状态的测试所表现出的症状,并且这些测试正在修改该状态,导致一个或多个测试失败。 / p>

答案 1 :(得分:1)

访问同一个文件应该不是问题。确保您没有cleanUp Fixture(TestSuite级别)来删除文件。因为从例外看起来该文件在运行测试后被删除。

同时读取操作也很好,完全合法。如果你的单元测试覆盖了文件,那么这就是一个问题。

答案 2 :(得分:1)

发生了什么事情,因为我使用相对路径来测试文件,出于某种原因,在批量运行单元测试时,测试运行器工作目录与运行单个测试时不同,因此无法找到。目录

所以我使用这个函数来构建测试文件的绝对路径:

    private string GetFilePath([CallerFilePath] string path = "")
    {
        return path;
    }

然后:

    string projectDir = Path.GetDirectoryName(GetFilePath());
    string testFile = Path.Combine(projectDir, @"Resources\test.png";

答案 3 :(得分:0)

我推测你的问题是,经过测试的方法之一改变了目录,给定了明确的"目录未找到"例外。文件锁定或任何并发问题都会导致所描述的行为,这是不可能的。

答案 4 :(得分:0)

如果单元测试你不应该测试File.Copy(或任何File类方法)是否有效,因为你没有编写该代码。相反,您应该测试您的代码是否与文件类型正确交互(即,当您调用&#34时,它是否传递了正确的源文件名,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参数(dependency injection)。在单元测试中,您可以使用模拟框架(如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以及如何调用它。由于您不再在单元测试之间共享真实文件,因此可以避免测试共享状态或可能锁定文件的可能性。

答案 5 :(得分:0)

正如您提到的in your answer一样,测试框架并非总是在将工作目录设置为构建输出文件夹的情况下运行测试。

要指示测试框架将构建工件或其他文件从构建输出中放置到测试目录中,您需要使用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);
}

还要确保您的文件带有“内容的构建操作”和“始终复制”标记。否则,它们将不在构建输出目录中,并且将无法复制到测试目录中。