用Monkey Patch在Python中使用语句

3

我正在使用 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 变成无操作状态,那么测试将如何进行? - Anand S Kumar
抱歉,Anand,我稍微修改了我的代码。但是目标是记录调用TestClass()的一些参数,然后立即退出with块。 - Jatin Kumar
我不确定我现在还理解你的问题。 - James Mills
2个回答

1
我认为你试图做的事情是被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),但该建议已被拒绝。


感谢您发布原始代码。很明显,我们肯定不能将整个块设置为无操作(no-op)。虽然如果可以接受的话,也可以通过查看我的答案来实现。 - Jatin Kumar

0

从@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!'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接