我最近一直在努力的程序中的一个常见任务是以某种方式修改文本文件.(嘿,我在Linux上.一切都是文件.我做大规模的系统管理员.)
但是代码修改的文件可能不存在于我的桌面盒上.如果它在我的桌面上,我可能不想修改它.
我已经阅读了Dive Into Python中的单元测试,在测试将十进制转换为罗马数字的应用程序(DintoP中的示例)时,我非常清楚我想要做什么.测试非常独立.您无需验证程序PRINTS是否正确,您只需要验证函数是否将正确的输出返回给定输入.
但是,在我的情况下,我们需要测试程序是否正确地修改了它的环境.这是我想出的:
1)在标准位置创建"原始"文件,可能是/ tmp.
2)运行修改文件的函数,将路径传递给/ tmp中的文件.
3)验证/ tmp中的文件是否正确更改; 相应的通过/失败单元测试.
这对我来说似乎很狡猾.(如果你想验证文件的备份副本是否正确创建等,甚至可以获得kludgier.)有没有人想出更好的方法?
你说的是一次测试太多了.如果您开始尝试通过说"让我们验证它正确地修改其环境"来攻击测试问题,那么您注定要失败.环境有数十种甚至数百种可能的变化.
相反,请查看程序的各个部分("单位").例如,您是否有一个确定必须写入文件的位置的函数?该功能的输入是什么?也许是一个环境变量,也许从配置文件中读取一些值?测试该功能,并不实际做任何修改文件系统的事情.不要传递"现实"值,传递易于验证的值.创建一个临时目录,用测试setUp
方法中的文件填充它.
然后测试写入文件的代码.只要确保它正在编写正确的内容文件内容.甚至不写入真正的文件系统!你不需要为此制作"假"文件对象,只需使用Python的方便StringIO
模块; 它们是"文件"界面的"真实"实现,它们不是您的程序实际要写入的那些.
最终,您将不得不测试最终的,实际上是实际连接的顶级函数,它传递真实的环境变量和真实的配置文件并将所有内容放在一起.但是不要担心要开始.首先,当您为较小的函数编写单独的测试时,您将开始学习技巧,并且创建测试模拟,假货和存根将成为您的第二天性.另一方面:即使你不能完全弄清楚如何测试一个函数调用,你也会非常自信地认为它调用的所有东西都能完美运行.此外,您会注意到测试驱动的开发会迫使您使API更清晰,更灵活.例如:测试调用的东西要容易得多open()
对来自某个地方抽象的对象的方法,而不是测试调用os.open
传递它的字符串的东西.该open
方法是灵活的; 它可以是伪造的,它可以以不同的方式实现,但字符串是一个字符串,os.open
并没有给你任何余地来捕捉它上面调用的方法.
您还可以构建测试工具,以便轻松完成重复性任务.例如,twisted提供了用于创建临时文件的工具,用于在其测试工具中内置的测试.使用自己的测试库测试工具或更大的项目来获得这样的功能并不罕见.
你有两个级别的测试.
过滤和修改内容.这些是"低级"操作,实际上并不需要物理文件I/O. 这些是测试,决策,替代等.应用程序的"逻辑".
文件系统操作.创建,复制,重命名,删除,备份.抱歉,这些都是正确的文件系统操作 - 很好 - 需要一个适当的文件系统进行测试.
对于这种测试,我们经常使用"模拟"对象.您可以设计一个"FileSystemOperations"类,它包含各种文件系统操作.你测试它以确保它做基本的读,写,复制,重命名等.这里没有真正的逻辑.只是调用文件系统操作的方法.
然后,您可以创建一个模拟各种操作的MockFileSystem.您可以使用此Mock对象来测试其他类.
在某些情况下,所有文件系统操作都在os模块中.如果是这种情况,您可以创建一个MockOS模块,其中包含您实际使用的操作的模拟版本.
将您的MockOS模块放在上面PYTHONPATH
,您可以隐藏真实的OS模块.
对于生产操作,您可以使用经过良好测试的"逻辑"类和FileSystemOperations类(或真正的OS模块).