8

我已经阅读了一些关于在 Python 单元测试方法中使用的相互矛盾的assert建议setUp。如果测试依赖的先决条件失败,我看不出测试失败的危害。

例如:

import unittest

class MyProcessor():
    """
    This is the class under test
    """

    def __init__(self):
        pass

    def ProcessData(self, content):
        return ['some','processed','data','from','content'] # Imagine this could actually pass

class Test_test2(unittest.TestCase):

    def LoadContentFromTestFile(self):
        return None # Imagine this is actually doing something that could pass.

    def setUp(self):
        self.content = self.LoadContentFromTestFile()
        self.assertIsNotNone(self.content, "Failed to load test data")
        self.processor = MyProcessor()

    def test_ProcessData(self):
        results = self.processor.ProcessData(self.content)
        self.assertGreater(results, 0, "No results returned")

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

这对我来说似乎是一件合理的事情,即确保测试能够运行。当由于设置条件而失败时,我们得到:

F
======================================================================
FAIL: test_ProcessData (__main__.Test_test2)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Projects\Experiments\test2.py", line 21, in setUp
    self.assertIsNotNone(self.content, "Failed to load test data")
AssertionError: unexpectedly None : Failed to load test data

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)
4

5 回答 5

10

的目的setUp是减少在安排阶段在测试类中的测试之间创建的样板代码。

在安排阶段,您: 设置运行测试代码所需的一切。这包括测试运行所需的依赖项、模拟和数据的任何初始化。

根据以上段落,您不应在setUp方法中断言任何内容。

如前所述;如果您不能创建测试前提条件,那么您的测试就被破坏了。为了避免这种情况,Roy Osherove 写了一本很棒的书,叫做:单元测试的艺术(为了完全披露 Lior Friedman(他是 Roy 的老板)是我的一个朋友,我与他们密切合作了 2 年多,所以我有点偏颇……)

基本上只有几个原因在安排阶段与外部资源进行交互(或与可能导致异常的事物),其中大多数(如果不是全部)与集成测试相关。

回到你的例子;有一种模式可以构建需要加载外部资源的测试(对于所有/大多数)。只是一个旁注;在决定应用此模式之前,请确保您不能将此内容作为 UT 类中的静态资源,如果其他测试类需要使用此资源,请将此资源提取到模块中。

以下模式降低了失败的可能性,因为您对外部资源的调用较少:

class TestClass(unittest.TestCase):

    def setUpClass(self):
        # since external resources such as other servers can provide a bad content
        # you can verify that the content is valid
        # then prevent from the tests to run  
        # however, in most cases you shouldn't.
        self.externalResourceContent = loadContentFromExternalResource()


    def setUp(self):
        self.content = self.copyContentForTest()

优点:

  1. 失败的机会更少
  2. 防止不一致行为(1. 某事/某人编辑了外部资源。2. 您在某些测试中未能加载外部资源)
  3. 更快的执行

缺点:

  1. 代码更复杂
于 2016-05-22T13:23:33.793 回答
5

setUp不是为了断言先决条件,而是为了创建它们。如果您的测试无法创建必要的夹具,则它已损坏,而不是失败。

于 2016-05-10T09:27:05.927 回答
3

来自Python 标准库文档

“如果setUp()方法在测试运行时抛出异常,框架会认为测试出错,runTest()方法不会被执行。如果setUp()成功,tearDown()方法无论 runTest() 成功与否都会运行。这样的测试代码工作环境称为夹具。

setUp() 方法中的断言异常将被 unittest 框架视为错误。不会执行测试。

于 2016-05-20T15:41:13.220 回答
2

您要避免在setUp(). 如果 setUp 失败,您的 tearDown 将不会被执行。

例如,如果您设置了一组数据库记录并且您的拆卸删除了这些记录,那么这些记录将不会被删除。

有了这个片段:

import unittest

class Test_test2(unittest.TestCase):

    def setUp(self):
        print 'setup'
        assert False

    def test_ProcessData(self):
        print 'testing'

    def tearDown(self):
        print 'teardown'

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

你只运行setUp()

$ python t.py 
setup
E
======================================================================
ERROR: test_ProcessData (__main__.Test_test2)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "t.py", line 7, in setUp
    assert False
AssertionError

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)
于 2016-05-23T07:56:50.563 回答
2

这里没有正确或错误的答案,这取决于您要测试的内容以及设置测试的成本。如果数据不符合预期,有些测试太危险而不允许尝试运行,有些测试需要使用该数据。

如果您需要在测试之间检查特定条件,您可以在 setUp 中使用断言,这有助于减少测试中的重复代码。然而,也使得在类或文件之间移动测试方法有点棘手,因为它们将依赖于具有等效的设置。它还可以为不太懂代码的测试人员突破复杂性的极限。

有一个测试单独检查这些启动条件并首先运行它会更干净一些,在每个测试之间可能不需要它们。如果您将其定义为 test_01_check_preconditions ,它将在任何其他测试方法之前完成,即使其余测试方法是随机的。然后,您还可以在某些条件下使用 unittest2.skip 装饰器。

更好的方法是使用 addCleanup 来确保重置状态,这里的好处是即使测试失败它仍然可以运行,您还可以让清理更加了解具体情况,因为您在您的上下文中定义它测试方法。

也没有什么可以阻止您在 unittest 类中定义方法来进行常见检查并在 setUp 或 test_methods 中调用它们,这有助于将复杂性保持在定义和管理的区域中。

也不要试图将 unittest2 子类化到一个简单的测试定义之外,我已经看到人们试图这样做以使测试变得简单并且实际上引入了完全意想不到的行为。

我想真正的收获是,如果你知道你为什么要使用它,并确保你记录你的原因,它可能没问题,如果你不确定,那么选择最简单最容易理解的选项,因为如果测试是无用的不容易理解。

于 2016-05-18T12:10:17.123 回答