简体   繁体   English

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

[英]When multiple unit tests copy the same file, running all unit tests fail

Description 描述

I am writing unit tests for a method, which copies a file from a source to a destination. 我正在为一个方法编写单元测试,该方法将文件从源复制到目标。 Basically it includes this code: 基本上它包括这个代码:

public void MyMethod() 
{
    // ...
    File.Copy(source, destination, true);
    // ...
}

In my unit test project, I have a test file: ( test.png ), which is located in the Resources folder of my unit test project. 在我的单元测试项目中,我有一个测试文件:( test.png ),它位于我的单元测试项目的Resources文件夹中。 And I've set the Copy to Output property to Always . 我将Copy to Output属性设置为Always

I have 3 unit tests which are testing this method. 我有3个单元测试正在测试这个方法。

When they hit the line of the code which copies the file: source = "Resources\\\\test.png" . 当他们点击复制文件的代码行时: source = "Resources\\\\test.png"

Issue 问题

When I run the unit test individually, they all pass and everything is fine. 当我单独进行单元测试时,它们都通过了,一切都很好。 However, when I run All Tests in Visual Studio, I get this run time error and unit tests fail: 但是,当我在Visual Studio中运行所有测试时,我得到此运行时错误并且单元测试失败:

System.IO.DirectoryNotFoundException System.IO.DirectoryNotFoundException

Could not find a part of the path 'Resources\\test.png'. 找不到路径'Resources \\ test.png'的一部分。

My Thoughts...(UPDATED) 我的想法......(更新)

  • Probably because Visual Studio runs each unit tests simultaneously in a separate thread and they all accessing the same file at the same time? 可能是因为Visual Studio在一个单独的线程中同时运行每个单元测试,并且它们都同时访问同一个文件?

  • I think for every unit test, Visual Studio is cleaning bin/Debug and bin/Release folders. 我认为对于每个单元测试,Visual Studio都是清理bin/Debugbin/Release文件夹。 Then it copies all the required project files in that folder. 然后,它复制该文件夹中的所有必需项目文件。 This causes sometimes the file actually does not exist? 这有时导致文件实际上不存在?

Question

How can I fix this problem? 我该如何解决这个问题?

Is there any settings of configurations to resolve this? 是否有任何配置设置来解决此问题?

How can I run all unit tests in Visual Studio (and Team City) when multiple unit tests are accessing the same file? 当多个单元测试访问同一个文件时,如何在Visual Studio(和Team City)中运行所有单元测试?

You could try to rule out the multi-threading issue by following the instructions from MSDN: Executing Unit Tests in parallel on a multi-CPU/core machine , setting parallelTestCount to 1 . 你可以尝试按照从指令排除多线程问题MSDN:多CPU /核机器上并行执行单元测试 ,设置parallelTestCount1 If the tests now pass, you've narrowed down the problem. 如果测试现在通过,你就缩小了问题范围。

However, if your tests are still failing when you run them in a group - and I think this is the more likely scenario -, then my advice would be to check for any state those tests are sharing. 但是,如果在组中运行它们时测试仍然失败 - 我认为这是更可能的情况 - 那么我的建议是检查这些测试共享的任何状态。 The pattern you describe (ie passes in isolation; fails when not in isolation) is a symptom typically exhibited by tests that are (incorrectly) sharing state, and that state is being modified by those tests, causing one or more tests to fail. 您描述的模式(即隔离传递;不隔离时失败)是通常由(错误地)共享状态的测试所表现出的症状,并且这些测试正在修改该状态,导致一个或多个测试失败。

Accessing the same file should not be a problem. 访问同一个文件应该不是问题。 Make sure you don't have a cleanUp Fixture(TestSuite level) to delete the file. 确保没有cleanUp Fixture(TestSuite级别)来删除文件。 Because from exception it looks like the file is being deleted after running a test. 因为从例外看起来该文件在运行测试后被删除。

Also concurrent read operation is fine and perfectly legal. 并发读操作也很好,完全合法。 If your unit tests are overwriting the file then it's a problem. 如果您的单元测试覆盖了文件,那么这是一个问题。

What happened was, since I was using relative path to the testing files, for some reason when running the unit tests in batch, test runner working directory is different than when running individual tests, hence it couldn't find the directory. 发生了什么事,因为我使用测试文件的相对路径,出于某种原因,在批量运行单元测试时,测试运行器工作目录与运行单个测试时不同,因此无法找到目录。

So I used this function to build the absolute path to the testing files: 所以我使用这个函数来构建测试文件的绝对路径:

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

Then: 然后:

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

I'd speculate that your problem is that one of the tested methods changes directory, given the explicit "Directory Not Found" exception. 我推测你的问题是,在给定明确的“找不到目录”异常的情况下,其中一个经过测试的方法会更改目录。 It's improbable that file locking or any concurrency problems would cause the behaviour described. 文件锁定或任何并发问题都不可能导致所描述的行为。

If unit testing you shouldn't really be testing whether File.Copy (or any of the File class methods) worked since you didn't write that code. 如果单元测试你不应该真正测试File.Copy(或任何File类方法)是否有效,因为你没有编写该代码。 Instead you should test whether your code interacts correctly with the File type (ie did it pass the correct source file name, desination file name and overwrite value when you called "Copy"). 相反,您应该测试您的代码是否与文件类型正确交互(即,当您调用“复制”时,它是否传递了正确的源文件名,desination文件名和覆盖值)。 First create an interface for the File class and a wrapper for it that implements the interface; 首先为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);
    }
}

You should then make the class you are testing include an IFileWrapper parameter (dependency injection) . 然后,您应该使您正在测试的类包含IFileWrapper参数(依赖注入) In your unit tests you can then use a mocking framework such as Moq or you could write your own mock; 在单元测试中,您可以使用模拟框架(如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;
    }
}

In real implementations pass in FileWrapper as the IFileWrapper parameter but in your unit tests pass in MockFileWrapper. 在实际实现中,将FileWrapper作为IFileWrapper参数传递,但在单元测试中传递MockFileWrapper。 By checking the properties of mockFileWrapper in your unit tests you can now determine whether you class calls Copy and how it is called. 通过在单元测试中检查mockFileWrapper的属性,您现在可以确定是否类调用Copy以及如何调用它。 Since you are no longer sharing a real file between your unit tests you will avoid the chance of the tests sharing state or potentially locking the file. 由于您不再在单元测试之间共享实际文件,因此可以避免测试共享状态或可能锁定文件的可能性。

As you mentioned in your answer , the test framework does not always run tests with the working directory set to your build output folder. 正如您在答案中提到的,测试框架并不总是在将工作目录设置为构建输出文件夹的情况下运行测试。

To instruct the test framework to place build artifacts or other files from your build output into the test directory, you need to use DeploymentItemAttribute . 要指示测试框架将构建输出中的构建工件或其他文件放入测试目录,您需要使用DeploymentItemAttribute For your case, you would do something like: 对于您的情况,您可以执行以下操作:

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);
}

Also ensure that your files are marked with a Build Action of Contents and Always Copy. 还要确保您的文件标记为内容构建操作和始终复制。 Otherwise, they won't be in the build output directory and won't be able to be copied to the test directory. 否则,它们将不在构建输出目录中,并且将无法复制到测试目录。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM