TDD:是否有集成测试,但没有单元测试
本文关键字:单元测试 集成测试 是否 TDD | 更新日期: 2023-09-27 18:24:56
技术堆栈:.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;
}
}
测试这些方法有什么价值吗?如果是这样的话,我该如何提取对文件系统的调用?
我的另一个问题是如何测试特定于图像文件类型(~200MB)的子类。我搜索过这个网站,发现了类似的问题,但没有一个涉及我在这个项目中使用的文件大小。类有集成测试(使用"黄金文件"),但没有单元测试,这合理吗?在这种情况下,我怎么能严格遵循TDD方法并首先编写一个失败的测试呢?
在回答您的第一个问题时,是的,测试这些方法是有价值的。我发布了一个库,它可以在不影响文件系统的情况下方便地做到这一点:https://bitbucket.org/mylesmcdonnell/mpm.io/wiki/Home
在(不是)回答您的第二个问题时,我需要查看一些代码,但我怀疑您可能需要对上述lib采取类似的方法。即定义接口,将代理定义为具体,将工厂定义为返回代理或mock。
我怎么能严格遵循TDD方法并首先编写一个失败的测试呢在这种情况下?
轻松!您模拟文件系统:)
这听起来可能需要做很多工作,但通常只需要实现一些方法,并根据需要进行扩展。在上述情况下。。。你只需要一个。
public interface IFileStore
{
Boolean FileExists(String path);
}
其想法是将您的文件工作委派到接口后面,并创建一个具体的实现来完成繁重的工作。基本上是适配器模式。
对于这种事情,我甚至不反对"穷人的DI",因为如果你的应用程序稍后调用容器,你就可以实现它。。。您可能会一直使用真实的文件系统。
public class BaseFile
{
private IFileStore _fileStore
public IFileStore FileStore
{
get
{
return _fileStore ?? (_fileStore = new ConcreteFileStore());
}
set
{
_fileStore = value;
}
}
//SNIP...
}
现在您有了一个可测试的实现,并且不必依赖任何"Golden"文件。
测试这些方法有价值
虽然现在这看起来像是为了微不足道的收益而做的额外工作,但添加BaseFile这样的测试应该在文件不存在时抛出FileNotFoundException至少可以实现两个目标:
定义预期行为列表
项目的新成员可以查看测试名称,以确定类的操作方式。他们将知道在每种情况下会发生什么——异常、null、默认结果等。
它还迫使你用通俗易懂的英语思考和定义你希望事情如何运作,而不是只在这里和那里加入条件和例外。这应该会在整个项目中产生一个非常一致的理念。
开发一套自动化回归测试
假设有人看到一些代码在特定条件下抛出异常,但他们认为做其他事情更明智(接受错误,但添加一个新的IsValid属性,这样消费者就可以知道构造/初始化是否成功)。如果他们做出这样的改变,测试将很快引起人们对这种改变的关注。事情的背后有一个有意识和有意的决定,人们可能已经开始依赖现有的行为——这种变化需要进一步讨论才能被接受。
至于你问题的第二部分,我认为Josh和Myles都已经提供了很好的建议。
使用接口模拟简单的文件系统调用似乎有些过头了。使用ITimeService模拟当前时间也是如此。我倾向于使用Func或Action,因为它要简单得多:
public static Func<string, bool> FileExists = System.IO.File.Exists;
public static Func<DateTime> GetCurrentTime = () => DateTime.Now;
由于这些都是公开的,我可以在单元测试中轻松地模拟。代码保持简单,不需要为简单的事情注入各种接口。对于流,我通常在单元tets中使用MemoryStream。
集成测试本身就有价值。如果像Joshs的回答中所解释的那样模拟文件系统,那么您真的不确定您的代码是否真的会在生产中运行。文件系统有很多隐藏的契约,这些契约对模拟来说并不重要。如果你的mock/fake显示出稍微不同的行为,你的代码可能会在你不知情的情况下开始依赖它。
只有集成测试才能确定某些事情。(集成测试也有缺点!)。