C#(单元测试):我需要使其无法通过代码写入文件

发布于 2024-10-12 06:40:09 字数 887 浏览 9 评论 0原文

基本上情况是这样的。我有以下内容:

public IService Service { get; set; } //Set to MyMockedService class.

public Boolean DoFoo()
{
    //possible other ways of returning true/false...

    Boolean success = true;

    //Get FileInfo[] items

    foreach (var item in items)
        DoOtherFoo(item);
}

public Boolean DoOtherFoo(FileInfo fileInfo)
{
    String filepath = //manipulate fileInfo.FullName;

    Byte[] file = Service.GetFile(filepath)
    try
    {
        WriteBinaryFile(filepath, file); //How can I force file writing to throw an exception
    }
    catch (Exception)
    {
        return false;
    }
}

基本上,在测试 DoFoo() 时,我有很多可能返回 true/false 的路径。我已经对所有这些文件进行了单元测试,除了最后一个...它尝试写入文件,如果其中一个文件由于某种原因无法写入,它就会失败并返回 false。起初,我想如果我尝试设置一个错误的文件名,例如“bad*file”,它会在 WriteFile 中引发异常,但我什至没有做到这一点,因为我无法使用非法字符创建 FileInfo 对象。所以我正在寻找另一种方法来实现它,这样就不可能写入文件,这样我就可以返回错误。

Basically, here's the situation. I have the following:

public IService Service { get; set; } //Set to MyMockedService class.

public Boolean DoFoo()
{
    //possible other ways of returning true/false...

    Boolean success = true;

    //Get FileInfo[] items

    foreach (var item in items)
        DoOtherFoo(item);
}

public Boolean DoOtherFoo(FileInfo fileInfo)
{
    String filepath = //manipulate fileInfo.FullName;

    Byte[] file = Service.GetFile(filepath)
    try
    {
        WriteBinaryFile(filepath, file); //How can I force file writing to throw an exception
    }
    catch (Exception)
    {
        return false;
    }
}

Basically, In testing DoFoo() I have a lot of paths that could return true/false. I've unit tested all of them except the final one... where it attempts to write the files and if even one of the files can't be written for some reason, it fails and sends back false. At first I figured if I tried to setup a bad filename such as "bad*file" it would throw an exception in the WriteFile but I didn't even get that far because I can't create a FileInfo object using illegal characters. So I'm looking for another way to make it so that it would be impossible to write the file so I can get back a false.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

┼── 2024-10-19 06:40:09

好的单元测试是孤立的。这意味着它们绝对独立于任何环境(文件、数据库、网络等)。如果你的代码使用文件来存储数据,你应该通过一些接口隐藏,生产和测试代码将使用不同的实现。生产实际上会写入文件,测试只会模拟它。

public interface IStorage
{
  bool StoreToFile(string path, string file, byte[] data);
}

public class Storage : IStorage
{
  public bool StoreToFile( ... )
  {
     return WriteToFile( ... );
  }
}

public class StorageMock : IStorage
{
   public bool StoreToFile (...)
   {
      return false;  //or true, depends on you test case
   }
}

现在,在测试中,您可以用假实现“替代”真实实现。这称为嘲笑。

考虑到“控制反转”的设计。还有很多框架允许您通过依赖项注入使用“控制投资”(StructureMap、Ninject、Wisdor)

Good unit tests are isolated ones. It means they are absolutely idependent of any environment (files, database, network etc.). If your code uses files to store data in it, you should hide by some interface, the production and test code would use different implmementation. Production will really do Write to file, test one will only emulate it.

public interface IStorage
{
  bool StoreToFile(string path, string file, byte[] data);
}

public class Storage : IStorage
{
  public bool StoreToFile( ... )
  {
     return WriteToFile( ... );
  }
}

public class StorageMock : IStorage
{
   public bool StoreToFile (...)
   {
      return false;  //or true, depends on you test case
   }
}

Now in tests you are able to "subtitue" real implmementaion with fake one. This is called mocking.

Desing that takes into considiration called "Inversion of Control". There are also a bunch of frameworks that allows you to use "Investion of Control" throught dependency injection (StructureMap, Ninject, Wisdor)

南街女流氓 2024-10-19 06:40:09

WriteBinaryFile 是同一个 中的方法吗?

您可以创建一个具有此唯一职责的对象,但从抽象开始:

public interface IBinaryFileWriter
{
    void WriteBinaryFile(string filepath, Byte[] file);
}

现在您可以将此依赖项注入到您正在测试的类中,最好作为构造函数参数。

在您的应用程序中,您将使用 IBinaryFileWriter 的实现来完成当前方法的操作。

不过,在测试中,您可以提供一个模拟,您可以将其配置为引发异常。

Is WriteBinaryFile a method in this same class?

You could create an object with this sole responsibility, but start with an abstraction:

public interface IBinaryFileWriter
{
    void WriteBinaryFile(string filepath, Byte[] file);
}

Now you can inject this dependency into the class that you are testing, preferably as a constructor argument.

In your application, you'll use an implementation of IBinaryFileWriter that does exactly what your current method does.

In tests, though, you can supply a mock that you can configure to throw an exception.

柏拉图鍀咏恒 2024-10-19 06:40:09

Your unit test can use System.IO.File.Open(...) with FileShare set to None. Any other process will fail to open that file.

如日中天 2024-10-19 06:40:09

这取决于 WriteBinaryFile 的内部结构。您需要在更细粒度的级别上测试 WriteBinaryFile。我认为这就是你的职责……我根本不认识这一点。

It depends on the internals of WriteBinaryFile. You need to be testing WriteBinaryFile at a more granular level. I assume that that's your function...I don't recognize that off the top of my head.

鸢与 2024-10-19 06:40:09

这是 FileInfo 的反编译代码

public FileInfo(string fileName)
{
    if (fileName == null)
    {
        throw new ArgumentNullException("fileName");
    }
    base.OriginalPath = fileName;
    string fullPathInternal = Path.GetFullPathInternal(fileName);
    new FileIOPermission(FileIOPermissionAccess.Read, new string[] { fullPathInternal }, false, false).Demand();
    this._name = Path.GetFileName(fileName);
    base.FullPath = fullPathInternal;
}

因此,在创建 FileInfo 后,您可以通过反射将 FullPath 和 OriginalPath 设置为无效文件路径,并使 WriteBinary 抛出异常,

就像这样:

 FileInfo info = new FileInfo("c:\\1.txt");
 info.GetType().BaseType.GetField(
      "FullPath", 
      BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance)
   .SetValue(info, "invalidpath");
 info.GetType().BaseType.GetField(
      "OriginalPath", 
       BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance)
   .SetValue(info, "invalidpath");

Here is the decompiled code for ctor of FileInfo

public FileInfo(string fileName)
{
    if (fileName == null)
    {
        throw new ArgumentNullException("fileName");
    }
    base.OriginalPath = fileName;
    string fullPathInternal = Path.GetFullPathInternal(fileName);
    new FileIOPermission(FileIOPermissionAccess.Read, new string[] { fullPathInternal }, false, false).Demand();
    this._name = Path.GetFileName(fileName);
    base.FullPath = fullPathInternal;
}

So after creating a FileInfo you can set the FullPath and OriginalPath to an invalid filepath by reflection and make the WriteBinary throwing exceptoin

Just like that:

 FileInfo info = new FileInfo("c:\\1.txt");
 info.GetType().BaseType.GetField(
      "FullPath", 
      BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance)
   .SetValue(info, "invalidpath");
 info.GetType().BaseType.GetField(
      "OriginalPath", 
       BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance)
   .SetValue(info, "invalidpath");
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文