技术堆栈:.NET 4,C#,NUnit
我正在尝试将测试驱动开发应用于执行图像处理的新项目。我有一个基类,包含共享文件I / O方法和子类,执行各种特定的处理算法。据我了解,单元测试不会触及文件系统或其他对象,并模拟发生的行为。我的基类只包含简单的访问器和简单的文件系统I / O调用。
public class BaseFile
{
public String Path { get; set; }
public BaseFile()
{
Path = String.Empty;
}
public BaseFile(String path)
{
if (!File.Exists(path))
{
throw new FileNotFoundException("File not found.", path);
}
Path = path;
}
}
测试这些方法有什么价值吗?如果是这样,我怎样才能抽象出对文件系统的调用?
我的另一个问题是如何测试特定于某种图像文件的子类(~200 MB)。我搜索了网站并找到similar questions,但没有处理我正在处理此项目的文件大小。一个类是否有可能进行集成测试(使用“黄金文件”),但没有单元测试?在这种情况下,我怎样才能严格遵循TDD方法并首先编写失败的测试?
答案 0 :(得分:4)
在回答您的第一个问题时,是的,测试这些方法是有价值的。我已经发布了一个库,可以在不实际访问文件系统的情况下完成这项工作:https://bitbucket.org/mylesmcdonnell/mpm.io/wiki/Home
在(不是)回答你的第二个问题我需要看一些代码,但我怀疑你可能需要对上面的lib采取类似的方法。即;定义接口,定义代理到具体,定义工厂以返回代理或模拟。
答案 1 :(得分:3)
我怎样才能严格遵循TDD方法并首先编写失败的测试 在这种情况下?
轻松!你嘲笑文件系统:)
这可能听起来像很多工作,但通常你只需要实现一些方法,并根据需要进行扩展。在上面的例子中......你只需要一个。
public interface IFileStore
{
Boolean FileExists(String path);
}
我们的想法是将您的文件工作委托给界面,并创建一个具体的实现来完成繁重的任务。基本上是Adapter pattern。
我甚至不反对“穷人的DI”这类事情,因为如果您的应用程序需要它,您可以稍后实现容器。在这种情况下......你可能总是会使用真正的文件系统。
public class BaseFile
{
private IFileStore _fileStore
public IFileStore FileStore
{
get
{
return _fileStore ?? (_fileStore = new ConcreteFileStore());
}
set
{
_fileStore = value;
}
}
//SNIP...
}
现在你有一个可测试的实现,你不必依赖任何“Golden”文件。
答案 2 :(得分:1)
测试这些方法有价值
虽然现在看起来似乎是微不足道的额外工作,但是当文件不存在时,添加像 BaseFile这样的测试会抛出FileNotFoundException 实现至少两个目标:
定义预期行为列表
项目的新手可以查看测试名称,以确定您的课程的运作方式。他们将知道在每种情况下会发生什么 - 例外,空,默认结果等。
它还会迫使你思考并用简单的英语定义你想要的东西如何操作,而不是只是在这里和那里抛出条件和例外。这应该会在您的项目中应用非常一致的理念。
开展一系列自动化回归测试
考虑到有人看到某些代码在特定条件下抛出异常,但他们认为做其他事情更明智(吃错误,但添加新的IsValid属性,以便消费者可以知道构造/初始化是否成功)。如果他们做出这样的改变,测试将非常迅速地引起对变化的关注。事情的背后有一个有意识和有意的决定,人们可能已经成长为依赖现有的行为 - 这种变化需要进一步讨论才能被接受。
关于你问题的第二部分,我认为Josh和Myles已经提供了合理的建议。
答案 3 :(得分:1)
使用接口模拟简单的文件系统调用似乎有点矫枉过正。对于使用ITimeService模拟当前时间的事情也是如此。我倾向于使用Func或Action,因为它更简单:
public static Func<string, bool> FileExists = System.IO.File.Exists;
public static Func<DateTime> GetCurrentTime = () => DateTime.Now;
由于这些是公开的,我可以在单元测试中轻松模拟。代码保持简单,无需为简单的事情注入各种接口。对于流,我通常在单位tets中使用MemoryStream。
答案 4 :(得分:0)
集成测试本身就有价值。如果你嘲笑文件系统,就像Joshs回答中所解释的那样,你真的不确定你的代码是否真的会在生产中运行。文件系统有很多隐藏的契约,这些契约对于模拟来说并不重要。如果你的模拟/假冒行为略有不同,你的代码可能会在你不知情的情况下开始依赖它。
只有集成测试才能确定某些事情。 (集成测试也有缺点!)。