2011-01-12 78 views
0

写一个文件是不可能的。基本上,这是情况。我有以下几点:C#(单元测试):我需要通过代码

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对象。所以我正在寻找另一种方式来制作它,这样就不可能编写这个文件,这样我就可以找回一个错误的文件。

+0

为什么不在运行应用程序时才打开文件。取决于它是什么文件,目前的应用程序可能会锁定它,因此您将无法写入它。 – melaos 2011-01-12 16:21:01

+0

你有没有看到我的答案? – 2011-01-12 17:41:23

回答

4

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

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

现在,在测试中,您可以用真假来“实现”真正的implmementaion。这就是所谓的嘲弄。

需要考虑的设计称为“控制反转”。也有框架的一堆,使您可以使用“控制Investion” throught依赖注入(StructureMap,Ninject,Wisdor)

0

它取决于WriteBinaryFile的内部。您需要以更细化的级别测试WriteBinaryFile。我认为这是你的功能......我不认为这是我的头顶。

1

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

+1

是的,任何其他进程*包括*单元测试的合法实例(可能是命令行构建,同时也是在VS内部构建和运行测试)。这就是隔离很重要的原因。单元测试的可重复性对他们的有用性至关重要。如果*有时*失败,开发人员将失去对它们的信任,纠正潜在问题要困难得多。 – 2011-01-12 16:35:09

3

WriteBinaryFile在这个相同的方法class

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

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

现在,你可以注入这种依赖关系到您正在测试的类,最好的构造函数的参数。

在您的应用程序中,您将使用IBinaryFileWriter的实现,该实现完全符合您当前的方法。

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

0

下面是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扔exceptoin

就像这样:

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