【问题标题】:Replacing python with_statement by setUp and tearDown in Unittest在Unittest中用setUp和tearDown替换python with_statement
【发布时间】:2020-09-18 09:12:34
【问题描述】:

在一个测试套件中,我有一些代码组织如下,上下文是一些在退出 with 块时被删除的持久对象:

class Test(TestCase):
    def test_action1(self):
        with create_context() as context:
            context.prepare_context()
            context.action1()
            self.assertTrue(context.check1())
            
    def test_action2(self):
        with create_context() as context:
            context.prepare_context()
            context.action2()
            self.assertTrue(context.check2())

很明显,代码在两个测试中都有一些重复的设置样板,因此我想使用 setUp() 和 tearDown() 方法来分解该样板。

但我不知道如何提取 with_statement。我想出的是这样的:

class Test(TestCase):
    def setUp(self):
        self.context = create_context()
        self.context.prepare_context()

    def tearDown(self):
        del self.context()

    def test_action1(self):
        self.context.action1()
        self.assertTrue(self.context.check1())
            
    def test_action2(self):
        self.context.action2()
        self.assertTrue(self.context.check2())

但我相信当测试失败时这并不等同,而且必须在 tearDown() 中明确删除也感觉不对。

将 with_statement 代码更改为 setUp() 和 tearDown() 样式的正确方法是什么?

【问题讨论】:

  • def Test(TestCase):def 还是class
  • @TomWojcik:class,是伪代码,实际代码要复杂得多。我会解决的。

标签: python python-unittest with-statement


【解决方案1】:

我不是 100% 确定 setUp() 和 tearDown(),但是在上下文管理器 __enter____exit__ 中定义的方法听起来就像他们做你想让他们做的事情(只是名称不同) :

class ContextTester():
    def __enter__(self):
        self.context = create_context()
        self.context.prepare_context()
        return self.context

    def __exit(self, exc_type, exc_value, exc_traceback):
        self.context.close()

def Test(TestCase):
    def test_action1(self):
        with ContextTester() as context:
            context.action1()
            self.assertTrue(context.check1())
            

【讨论】:

  • 是的,我也想过,但这意味着将所有测试样板代码移至上下文管理,并完全停止使用旧的 setUp()/tearDown()。这可能是要走的路。
  • 嗯,我相信这是上下文管理器的意图。它们允许您排除重复的设置/拆卸代码
  • 是的,但在我的情况下,这意味着将现有的上下文管理器包装在用户定义的上下文管理器中。我还可能不得不处理几个with 上下文。它可能会很快变得复杂。同样为了清理我现有的上下文,我可能应该向它提供传递给__exit__ 的参数,比如明确调用self.context.__exit__() 而不是调用close()
【解决方案2】:

您可能想为此使用contextlib.ExitStack

一种上下文管理器,旨在以编程方式轻松组合其他上下文管理器和清理功能,尤其是那些可选的或由输入数据驱动的功能。

import contextlib
from unittest import TestCase


class Test(TestCase):
    def setUp(self) -> None:
        stack = contextlib.ExitStack()
        self.context = stack.enter_context(create_context())  # create_context is your context manager
        self.addCleanup(stack.close)

    def test_action1(self):
        self.context.prepare_context()
        self.context.action1()
        self.assertTrue(self.context.check1())

或者,如果您想对拆卸进行一些控制或使用多个上下文管理器,这会更好

import contextlib
from unittest import TestCase


class Test(TestCase):
    def setUp(self):
        with contextlib.ExitStack() as stack:
            self.context = stack.enter_context(create_context())  # create_context is your context manager
            self._resource_stack = stack.pop_all()

    def tearDown(self):
        self._resource_stack.close()

    def test_action1(self):
        self.context.prepare_context()
        self.context.action1()
        self.assertTrue(self.context.check1())

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-05
    • 2015-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-14
    相关资源
    最近更新 更多