Python单元测试代码,调用OS / Module级python函数

时间:2013-02-19 11:52:52

标签: python unit-testing python-2.7 python-unittest

我有一个python模块/脚本,可以完成其中的一些

  1. 在脚本内的各种嵌套级别,我接受命令行输入,验证它们,应用合理的默认值
  2. 我还检查是否存在一些目录
  3. 以上只是两个例子。我试图找出测试这个的最佳“策略”。我所做的是我在我的模块中围绕raw_inputos.path.exists构建了包装函数,然后在我的测试中我覆盖这两个函数以从我的数组列表中获取输入或者做一些 mocked 行为。这种方法有以下缺点

    1. 为了测试而存在包装函数,这会污染代码
    2. 我必须记住每次都在代码中使用包装函数而不只是调用os.path.existsraw_input
    3. 有什么好的建议吗?

3 个答案:

答案 0 :(得分:2)

解决方案1:我会做这样的事情,因为它有效:

def setUp(self):
    self._os_path_exists = os.path.exists
    os.path.exists = self.myTestExists # mock

def tearDown(self):
    os.path.exists = self._os_path_exists

不太好。

解决方案2:正如你所说,重组代码不是一个选项,对吧? 理解和不直观会使情况变得更糟。

答案 1 :(得分:2)

简短的回答是monkey patch这些系统调用。

How to display the redirected stdin in Python?

的答案中有一些很好的例子

以下是raw_input()使用lambda的一个简单示例,该示例会抛弃提示并返回我们想要的内容。

受测试系统

$ cat ./name_getter.py
#!/usr/bin/env python

class NameGetter(object):

    def get_name(self):
        self.name = raw_input('What is your name? ')

    def greet(self):
        print 'Hello, ', self.name, '!'

    def run(self):
        self.get_name()
        self.greet()

if __name__ == '__main__':
    ng = NameGetter()
    ng.run()

$ echo Derek | ./name_getter.py 
What is your name? Hello,  Derek !

测试用例:

$ cat ./t_name_getter.py
#!/usr/bin/env python

import unittest
import name_getter

class TestNameGetter(unittest.TestCase):

    def test_get_alice(self):
        name_getter.raw_input = lambda _: 'Alice'
        ng = name_getter.NameGetter()
        ng.get_name()
        self.assertEquals(ng.name, 'Alice')

    def test_get_bob(self):
        name_getter.raw_input = lambda _: 'Bob'
        ng = name_getter.NameGetter()
        ng.get_name()
        self.assertEquals(ng.name, 'Bob')

if __name__ == '__main__':
    unittest.main()

$ ./t_name_getter.py -v
test_get_alice (__main__.TestNameGetter) ... ok
test_get_bob (__main__.TestNameGetter) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

答案 2 :(得分:1)

Johnnysweb充分利用您需要做的事情,但您可以导入并使用mock,而不是自己动手。 Mock专为单元测试而设计,可以非常简单地完成您正在尝试的操作。它内置于Python 3.3。

例如,如果要运行替换os.path.isfile的单元测试并始终返回True:

try:
    from unittest.mock import patch
except ImportError:
    from mock import patch

class SomeTest(TestCase):

    def test_blah():
        with patch("os.path.isfile", lambda x: True):
            self.assertTrue(some_function("input"))

这可以为您节省大量的样板代码,而且它非常易读。

如果您需要更复杂的东西,例如,替换supbroccess.check_output,您可以创建一个简单的辅助函数:

def _my_monkeypatch_function(li):
     x,y = li[0], li[1]
     if x == "Reavers":
        return "Gorram"
     if x == "Inora":
        return "Shiny!"
     if x == y:
        return "The Ballad of Jayne"

def test_monkey():
     with patch("subprocess.check_output", _my_monkeypatch_function):
            assertEquals(subprocess.check_output(["Mudder","Mudder"]),
                                                 "The Ballad of Jayne")