【问题标题】:Monkey patch python with statement带有语句的猴子补丁python
【发布时间】:2015-09-03 23:18:34
【问题描述】:

我正在使用 py.test 进行我的 python 单元测试。考虑以下代码:

def mytest():
    "Test method"
    print "Before with statement"
    with TestClass('file.zip', 'r') as test_obj:
        print "This shouldn't print after patching."
        # some operation on object.
    print "After with statement."

是否可以对 TestClass 类进行猴子补丁,以便 with 块中的代码变为 noop

比如打补丁后的输出应该是:

Before with statement
After with statement

我知道我可以修补 mytest 函数本身,但这是为了获得更好的测试覆盖率。

我已经尝试过,以下几行中的某些内容但无法正常工作。

class MockTestClass(object):
    def __init__(self, *args):
        print "__init__ called."

    def __enter__(self):
        print "__enter__ called."
        raise TestException("Yeah done with it! Get lost now.")

    def __exit__(self, type, value, traceback):
        print "__exit__ called."

module_name.setattr('TestClass',  MockTestClass)

【问题讨论】:

  • 如果 zipFile.ZipFile 变为 no-op ,那么测试将如何进行?
  • 抱歉,Anand,我稍微修改了我的代码。但目的是记录 TestClass() 是用一些参数调用的,然后它应该立即退出 with 块。
  • 我不确定我是否理解你的问题了。

标签: python pytest with-statement monkeypatching test-coverage


【解决方案1】:

从@Peter 的回答中可以清楚地看出,我们不能将整个块设为noop。 我最终为我的用例做了以下工作。

# Module foo.py
class Foo(object):
    def __init__(self):
        print "class inited"

    def __enter__(self):
        print "entered class"
        return None

    def foo(self):
        raise Exception("Not implemented")

    def __exit__(self, type, value, traceback):
        print "exited class"
        return True

----------------------------
# Module FooTest
import foo

class FooTest(object):
    def __init__(self):
        print "class inited"

    def __enter__(self):
        print "entered class"
        return None

    def __exit__(self, type, value, traceback):
        print "exited class"
        return True

try:
    foo.Foo()
    print "It shouldn't print"
except:
    print "Expected exception"
setattr(foo, 'Foo', FooTest)
print "Patched"
with foo.Foo() as a:
    a.foo()
    print "It shouldn't print"
print 'Test passed!'

【讨论】:

    【解决方案2】:

    我认为 Python 语言规范不允许您尝试做的事情。

    正如您在PEP-343 中看到的,“with”语句的定义不允许任何提前退出上下文的尝试:

    mgr = (EXPR)
    exit = type(mgr).__exit__  # Not calling it yet
    value = type(mgr).__enter__(mgr)
    exc = True
    try:
        try:
            VAR = value  # Only if "as VAR" is present
            BLOCK
        except:
            # The exceptional case is handled here
            exc = False
            if not exit(mgr, *sys.exc_info()):
                raise
            # The exception is swallowed if exit() returns true
    finally:
        # The normal and non-local-goto cases are handled here
        if exc:
            exit(mgr, None, None, None)
    

    有人提议将其更改为您需要的函数 (PEP-377),但已被拒绝。

    【讨论】:

    • 感谢您发布原始代码。很明显,我们当然不能将整个块设为无操作。虽然如果可以接受,也可以到达那里,检查我的答案。
    猜你喜欢
    • 2012-12-18
    • 1970-01-01
    • 1970-01-01
    • 2011-04-15
    • 1970-01-01
    • 2012-12-12
    • 2017-10-05
    • 2016-09-01
    • 2012-09-16
    相关资源
    最近更新 更多