文件系统覆盖-用于单元测试的文件系统分层
fso的Python项目详细描述
文件系统覆盖(fso)允许无副作用的单元测试 文件I/O操作。它通过在 本地文件系统,允许读取访问,但存储 记忆中的修改。可以检查这些内存中的更改,以 验证单元测试,当测试完成时,对 文件系统将被蒸发并吹到海里(引用 Stanley Goodspeed博士: http://www.youtube.com/watch?v=K-uEbYq9kNU&t=6m29s)。
tl;dr
安装:
$ pip install fso
使用:
importunittest,fsoclassMyTest(unittest.TestCase):defsetUp(self):self.fso=fso.push()deftearDown(self):fso.pop()deftest_fs_changes(self):self.assertFalse(os.path.exists('/etc/foobar.conf'))withopen('/etc/foobar.conf','wb')asfp:fp.write('some-data')self.assertTrue(os.path.exists('/etc/foobar.conf'))self.assertEqual(open('/etc/foobar.conf','rb').read(),'some-data')# BUT, when testing ends, /etc/foobar.conf will not exist! *awesome*! :)# you can also check that the expected changes are there (noting# that all paths are absolutized, dereferenced, and normalized):self.assertEqual(self.fso.changes,['add:/etc/foobar.conf',])
概述
传统上,在文件系统上测试I/O操作需要 修改实现以便有一个可插入的层 在执行测试时用mock替换的文件操作 (http://stackoverflow.com/questions/2655697/python-unittest-howto)。
这是一个糟糕的方法,因为它意味着真正的代码 没有被执行,很可能隐藏了一些真正的错误。
作为替代方案,fso包将 低级文件系统调用并缓存内存中的更改,从不 实际上是在修改文件系统。
虽然这是一个非常“纯”的方法,但有一些问题… 因此,目前只支持基本的文件操作(例如 创建和删除文件和目录)–如果要执行更多操作 诸如打开unix域套接字和使用 阻止特殊设备,FSO可能无法完成任务。但是,如果你 别介意,请通过以下两种方式帮助识别这些漏洞 发布或提供修补程序…任何贡献都将合并并 非常感谢!
支持的操作
目前,只有以下I/O函数具有替换项 实施:
- 内置打开
- 操作系统符号链接
- 操作系统状态
- 操作系统lstat
- 操作系统取消链接
- 操作系统删除
- 操作系统列表目录
- os.mkdir
- os.makedirs
- os.rmdir
- OS.PATION存在
- 操作系统访问
- os.path.isLink
大多数其他I/O操作都构建在这些操作之上,因此 隐式地与fso一起工作。但是,因为它们使用任何 检测的函数当前在全局范围内,这意味着 它们与fso覆盖层的multiple级别不兼容。 因为这不是典型的fso用例,所以这被认为是 可接受的权衡。
支持但仅在使用 单个活动FSO层:
- 操作系统漫游
- os.path.isdir
- os.path.isfile
已知限制
- 文件权限当前未被强制(并且可能是过度的)。 和覆盖的目录报告模式0700,以及覆盖的文件 和符号链接报告模式0600。
- “u”和“ru”的文件打开模式被静默地视为“r”。
- 以下属性不可用/不受管理: *圣伊诺 *圣德夫 *圣林 *圣尤德 *圣吉德 *圣阿提姆 *时间 *圣克蒂姆
- 由于更改是显式存储在内存中的,因此 本地计算机的内存将导致问题。
- 下列文件系统项类别将不起作用: *插座 *阻止特殊设备文件 *字符专用设备文件 *fifos(命名管道)
用法
FSO支持上下文管理器!在大多数情况下,这实际上是 推荐。原因是一些单元测试框架,比如 鼻子,如果fso层仍然存在,不要很好地报告错误 活跃。使用上下文管理器将确保fso 在需要报告错误之前卸载。示例:
importunittest,fsoclassTestWithContextManager(unittest.TestCase):deftest_with_cm(self):self.assertFalse(os.path.exists('no-such-file'))withfso.push()asoverlay:self.assertFalse(os.path.exists('no-such-file'))withopen('no-such-file','wb')asfp:fp.write('created')os.unlink('/etc/hosts')os.mkdir('/tmp/my-test-directory')self.assertTrue(os.path.exists('no-such-file'))self.assertEqual(overlay.changes,['del:/etc/hosts','add:/path/to/cwd/no-such-file','add:/tmp/my-test-directory',])self.assertFalse(os.path.exists('no-such-file'))self.assertFalse(os.path.exists('/etc/my-test-directory'))self.assertTrue(os.path.exists('/etc/hosts'))