【问题标题】:How do I override imports of other files for unit testing如何覆盖其他文件的导入以进行单元测试
【发布时间】:2016-06-17 13:44:09
【问题描述】:

我目前正在尝试为 Main.py 的 main() 函数编写单元测试

这是我的 Main.py 的简化版本:

from Configuration import Configuration # Configuration.py is a file in the same dir

def main():
  try:
    Configuration('settings.ini')
  except:
    sys.exit(1) # Test path1
  sys.exit(0) # Test path2

if __name__ == '__main__':
    main()

在我的Unit Tests\MainUnitTests.py 中,我想导入..\Main.py 并伪造Configuration 类,以便我可以点击Test path1Test path2

我发现我可以通过以下方式断言sys.exit()

with self.assertRaises(SystemExit) as cm:
  main()
self.assertEqual(cm.exception.code, 1)

但我无法覆盖from Configuration import Configuration

想法?

到目前为止,我在Unit Tests\MainUnitTests.py 中尝试了以下内容:

class FakeFactory(object):
  def __init__(self, *a):
    pass

sys.modules['Configuration'] = __import__('FakeFactory')

class Configuration(FakeFactory):
  pass

另一个演示示例:

foo.py:

from bar import a,b

x = a()

class a(object):
  def __init__(self):
    self.q = 2

y = a()

print x.q, y.q # prints '1 2' as intended

b() # I want this to print 2 without modifying bar.py

bar.py:

class a(object):
  def __init__(self):
    self.q = 1

def b():
  t = a()
  print t.q

【问题讨论】:

  • 看看stackoverflow.com/questions/5626193/what-is-a-monkey-patch 实际上它是对您的问题的回答,包括示例
  • @farincz 猴子补丁似乎不起作用。在 MainUnitTest.py 我正在导入 Configuration.py 然后在它之后创建一个 class Configuration(FakeFactory) ...但是 Main.py's main() 仍在使用 Configuration.py 中的 Configuration 类,这两个文件似乎没有共享相同的全局名称空间
  • 您必须在其上导入配置和补丁配置属性,并且您必须在 main 是第一个 importet 之前执行此操作!比它应该工作
  • @farincz 仍然无法正常工作......请参阅我的问题中的Another example for demonstration
  • 当您使用导入 from bar import a 时,它会将 a 直接导入命名空间,因此猴子补丁 bar 将无济于事,您需要改写 main.a

标签: python python-unittest


【解决方案1】:

当你使用导入时

from bar import a

它直接将名称导入到模块中,所以猴子补丁bar 无济于事,您需要直接在主文件中覆盖a

def fake_a():
    print("OVERRIDEN!")

main.a = fake_a 

知道 unittest 在the mock subpackage 中为此提供了帮助函数,我相信您可以执行以下操作:

from unittest.mock import patch

...
with patch("main.a", fake_a) as mock_obj: #there are additional things you can do with the mock_obj
    do_stuff()

这将在您使用configuration 的第一个示例中起作用,因为需要修补的类未在全局范围内使用,尽管foo 在加载后立即使用bar.a,因此您需要对其进行修补之前甚至加载foo

from unittest.mock import patch

...
with patch("bar.a", fake_a) as mock_obj: #there are additional things you can do with the mock_obj
    import foo #now when it loads it will be loaded with the patched name

但是在这种情况下,foo.a 不会在 with 块的末尾恢复,因为它不能被 unittest 捕获...我真的希望您的实际用例不会使用这些东西在模块级别打补丁。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-08
    • 2011-03-14
    • 2013-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-04
    • 1970-01-01
    相关资源
    最近更新 更多