测试驱动开发:一种测试文件系统操作的好的/可接受的方法吗?


14

目前,我正在一个项目中,该项目根据文件系统的内容生成一个表(除其他外),然后对发现的内容进行一些元数据修改。问题是:应该如何围绕此编写测试或进行设置?有没有简单的方法可以模拟出来?还是应该设置一个“沙盒”?

Answers:


13

就像您在TDD中始终使用外部资源一样:您为文件系统操作创建一个或多个接口,然后“模拟”它们。您想测试“表生成器”和元数据修改代码,而不是文件系统操作本身(很可能您正在使用现成的库实现来访问文件系统)。


TDD不建议模拟被测单元的实现。参见(e,g)solnic.eu/2014/05/22/mocking-and-ruby.html
soru 2015年

1
@soru:这不是这个答案的建议。建议先创建接口,然后模拟接口。因此,您确实要测试业务逻辑,而不是文件系统接口。
sleske 2015年

5
但这听起来像是根据文件和目录定义了业务逻辑。所以调用文件系统API的东西对的东西,需要测试。测试任何相关的非文件业务逻辑无需模拟;只是测试一下。
soru 2015年

@soru对,因此您在文件和文件夹周围创建了一个薄层,并定义了一个接口,以使所有特定于域的操作都在客户端,而实现方面则微不足道,可以确信它无需单元测试即可工作(集成测试将仍然是必需的)。与ui代码中的“低级对话框”或在持久对象上运行的代码中使用模拟存储库类似。
Jules

2
因此,我们有效地放弃了测试与文件系统,数据库等交互的实际类的工作……相反,我们创建了一个具有与模拟/存根相同的接口的不同实现,但是我们留下的实际类没有任何类型的单元测试,因为我们认为我们不能对其进行单元测试,而应该进行集成测试来对其进行测试。它是否正确?
Andrew Savinykh

11

拥有“测试”文件系统有什么问题?

创建一个模板文件夹/目录结构,其内容足以测试您的操作。

在设置单元测试期间,请复制此初始结构(建议您将模板压缩并解压缩到测试区域中)。运行测试。拆除期间将整个内容删除。

模拟的问题是,首先,属于您项目的文件系统,操作系统和数据库并没有真正符合外部资源的条件,其次,模拟低级系统调用既费时又容易出错。


5
与使用真实文件系统相比,模拟文件系统操作将创建运行速度更快(!)的测试,并且如果这更“容易出错”,则值得商,,我会说这取决于实现方式。尽管如此,我认为您的建议对于创建自动化集成测试是很好的(通常在不执行TDD时会首先执行)。但是OP专门要求提供TDD,而TDD单元测试必须快速。
布朗

1
我认为,如果模拟了文件系统,理想情况下应该由某个小组编写和维护整个API,因为如果对文件系统做任何重要的事情,那是在重新发明轮子。
Frank Hileman

2
@Doc Brown-我有点假设他想执行dir,删除和重命名类型操作,所有这些操作都带有边缘情况,很难模拟。同样,在现代硬件上,将一些小文件解压缩到目录中仅比加载Java类(毕竟是所有IO)慢一点。
James Anderson

我对测试文件系统的思考越多,我就越喜欢它。
Frank Hileman 2015年

3

这是您绝对需要进行集成测试的事情,因为现实世界中的文件系统具有各种奇怪的行为(例如,Windows不允许在任何程序(包括删除程序)打开的情况下删除文件)。

因此,TDD方法是首先编写集成测试(严格来说,TDD没有“单元测试”和“集成测试”的不同概念;它们只是测试)。这很可能就足够了;这样工作就完成了,停下来,回家

如果不是这样,将存在一些内部复杂性,很难通过整理文件来进行适当的测试。在这种情况下,您只需消除这种复杂性,将其放入类中,然后为该类编写单元测试。您很可能会发现该通用类在数据库,xml文件等情况下也是可用的。

在任何情况下,您都不会拿出正在编写的代码的基本核心并将其“模拟”出来,以编写可通过的测试,无论被测单元是否错误。


这个答案对我来说确实是一个角度- 'unit test' and 'integration test'; they are just tests.我认为现实中,这将是针对我的案例的最佳解决方案-我确实需要测试我用于边缘案例的文件系统库,以及应用程序应如何响应那些。如果我切换到其他文件系统库,则不需要重写大量的模拟/测试代码来使用新库,但是拥有测试文件夹结构和集成测试将使这一过程变得更加简单。
tehDorf

2

我们将您的问题理解为“一种测试/依赖于文件系统操作的类的良好/可接受的方法”。我不认为您要测试操作系统的文件系统。

为了使“文件系统操作的接口并“模拟它们”的接口”工作尽可能地小,如@Doc Brown回答所建议的那样,请使其尽可能小,最好使用Java 二进制流文本阅读器(或c#或您正在使用的编程语言),而不是使用文件与文件名,直接在您的TDD开发类。

例:

使用Java我实现了一个类CsvReader

public class CsvReader {
    private Reader reader;

    public CsvReader(Reader reader) {
        this.reader = reader;
    }
}

为了测试,我在内存中使用了这样的数据

String contentOfCsv = "TestColumn1;TestColumn2\n"+
    "value1;value2\n";

CsvReader sut = new CsvReader(java.io.StringReader(contentOfCsv));

或将测试数据嵌​​入资源中

CsvReader sut = new CsvReader(getClass().getResourceAsStream("/data.csv"));

在生产中我使用文件系统

CsvReader sut = new CsvReader(new BufferedReader( new FileReader( "/import/Prices.csv" ) ));

这样,我的CsvReader并不依赖于文件系统,而是依赖于存在文件系统实现的抽象“阅读器”。


2
这里唯一的问题是,OP是不说话的文件操作,但文件系统的操作和元数据操作-我想他的意思是像列出所有目录中的文件,更新的所有图片文件等一些EXIF信息
布朗博士

这是对的。
Kirbinator 2013年

1
您可以创建具有方法String [] List(String directory);的IDirectoryLister。然后FakeDirectoryLister可以通过返回新的String [] {“。”,“ ..”,“ foo.bat”,“ bar.exe”}来实现该方法。
安德斯·林登

0

为文件系统操作创建包装器。在测试中,传入一个实现与包装器相同的接口的模拟程序。在生产中,传递包装纸。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.