C#(单元测试):我需要通过代码
写一个文件是不可能的。基本上,这是情况。我有以下几点: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对象。所以我正在寻找另一种方式来制作它,这样就不可能编写这个文件,这样我就可以找回一个错误的文件。
良好的单元测试是孤立的。这意味着他们完全相信任何环境(文件,数据库,网络等)。如果你的代码使用文件来存储数据,你应该隐藏某个接口,生产和测试代码会使用不同的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)
它取决于WriteBinaryFile的内部。您需要以更细化的级别测试WriteBinaryFile。我认为这是你的功能......我不认为这是我的头顶。
您的单元测试可以使用System.IO.File.Open(...)并将FileShare
设置为None
。任何其他进程将无法打开该文件。
是的,任何其他进程*包括*单元测试的合法实例(可能是命令行构建,同时也是在VS内部构建和运行测试)。这就是隔离很重要的原因。单元测试的可重复性对他们的有用性至关重要。如果*有时*失败,开发人员将失去对它们的信任,纠正潜在问题要困难得多。 – 2011-01-12 16:35:09
是WriteBinaryFile
在这个相同的方法class
?
您可以创建这个唯一的责任对象,但是有一个抽象的开始:
public interface IBinaryFileWriter
{
void WriteBinaryFile(string filepath, Byte[] file);
}
现在,你可以注入这种依赖关系到您正在测试的类,最好的构造函数的参数。
在您的应用程序中,您将使用IBinaryFileWriter
的实现,该实现完全符合您当前的方法。
但是在测试中,您可以提供一个可以配置为引发异常的模拟。
下面是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");
为什么不在运行应用程序时才打开文件。取决于它是什么文件,目前的应用程序可能会锁定它,因此您将无法写入它。 – melaos 2011-01-12 16:21:01
你有没有看到我的答案? – 2011-01-12 17:41:23