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

4

5 回答 5

4

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

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)使用“控制投资”

于 2011-01-12T16:28:31.790 回答
3

WriteBinaryFile方法一样吗class

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

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

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

在您的应用程序中,您将使用与IBinaryFileWriter当前方法完全相同的实现。

但是,在测试中,您可以提供一个可以配置为抛出异常的模拟。

于 2011-01-12T16:27:35.563 回答
1

您的单元测试可以使用System.IO.File.Open(...)FileShare设置为None. 任何其他进程都将无法打开该文件。

于 2011-01-12T16:22:50.037 回答
0

这取决于 WriteBinaryFile 的内部结构。您需要在更细粒度的级别上测试 WriteBinaryFile。我认为那是你的功能......我不知道这是我的头顶。

于 2011-01-12T16:22:40.573 回答
0

这是 FileInfo 的 ctor 的反编译代码

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");
于 2011-01-12T16:43:40.717 回答