Unittest测试订单

时间:2010-11-04 09:32:43

标签: python unit-testing

我如何确定unittest方法的顺序?字母或数字前缀是否正确?

class TestFoo(TestCase):
    def test_1(self):
        ...
    def test_2(self):
        ...

class TestFoo(TestCase):
    def test_a(self):
        ...
    def test_b(self):
        ...

18 个答案:

答案 0 :(得分:71)

没有理由认为你不能在之前的测试中进行构建,或者应该从头开始重建它以进行下一次测试。至少通常没有理由,但人们只是自信地说“你不应该”。这没用。

总的来说,我厌倦了在这里阅读太多的答案,基本上说“你不应该这样做”而不是提供任何关于如何最好地做到这一点的信息,如果在提问者判断中有充分的理由这样做的话。如果我想要某人就我是否应该做某事发表意见,那么我会就这是否是一个好主意征求意见。

这样,如果你读到了说loadTestsFromTestCase及其所谓的内容,它最终会以类方法字典中遇到的任何顺序扫描具有某种名称模式的方法,因此基本上按键顺序。它接受这些信息并制作一个测试套件,将其映射到TestCase类。给它一个按照你想要的顺序排列是一种方法来做到这一点。我不太确定最有效/最干净的方法,但这确实有效。

答案 1 :(得分:64)

您可以通过将sortTestMethodsUsing设置为None来禁用它: http://docs.python.org/2/library/unittest.html#unittest.TestLoader.sortTestMethodsUsing

对于纯粹的单元测试,你们是对的;但是对于组件测试和集成测试...... 我不同意你对国家一无所知。 如果您正在测试州,该怎么办? 例如,您的测试验证服务是否在安装时自动启动。如果在您的设置中,您启动了服务,然后执行断言,那么您不再测试状态,而是在测试“服务启动”功能。

另一个例子是当您的设置需要很长时间或需要大量空间时,经常运行设置变得不切实际。

许多开发人员倾向于使用“unittest”框架进行组件测试......所以停下来问问自己,我是在进行单元测试还是组件测试。

答案 2 :(得分:12)

如果使用'nose'并将测试用例编写为函数(而不是某些TestCase派生类的方法),'nose'不会调整顺序,而是使用函数的顺序。文件。为了使assert_ *方法更方便而不需要子类TestCase,我通常使用numpy中的测试模块。示例:

from numpy.testing import *

def test_aaa():
    assert_equal(1, 1)

def test_zzz():
    assert_equal(1, 1)

def test_bbb():
    assert_equal(1, 1)

使用''nosetest -vv''运行它会给出:

test_it.test_aaa ... ok
test_it.test_zzz ... ok
test_it.test_bbb ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.050s
OK

请注意所有那些认为不应该订购单元测试的人:虽然单元测试应该是独立的并且可以独立运行,但是你的函数和类通常不是独立的。他们宁愿建立另一个从更简单/更低级别的功能到更复杂/更高级别的功能。当你开始优化低级功能并搞砸时(就我而言,我经常这样做;如果你不这样做,你可能不需要进行单元测试;-)那么它对于诊断原因来说要好得多,当简单函数的测试首先出现时,测试依赖于这些函数的函数。如果测试按字母顺序排序,真正的原因通常会在100个失败的断言中被淹没,这些断言不存在,因为被测试的函数有错误,但是因为它所依赖的低级函数有。

这就是为什么我希望按照我指定的方式对单元测试进行排序:不使用在以后的测试中在早期测试中构建的状态,而是作为诊断问题的非常有用的工具。

答案 3 :(得分:11)

为什么需要特定的测试订单?测试应该是隔离的,因此应该可以按任何顺序运行,甚至可以并行运行。

如果您需要测试用户取消订阅之类的内容,测试可以创建一个带有测试订阅的新数据库,然后尝试取消订阅。这种情况有其自身的问题,但最终它比测试依赖于彼此更好。 (请注意,您可以分解常见的测试代码,这样您就不必重复数据库设置代码或创建令人作呕的测试数据。)

答案 4 :(得分:8)

我半信半疑地认为不能下令进行测试。在某些情况下,它有助于(它更容易死!)将它们按顺序排列......毕竟这就是UnitTest中“单位”的原因。

那说一个替代方法是使用模拟对象来模拟和修补应该在测试的特定代码之前运行的项目。您还可以在其中放置一个虚拟函数来修补代码。有关更多信息,请查看Mock,它现在是标准库的一部分。 Mock

以下是一些YouTube视频,如果您之前没有使用过Mock。

Video 1

Video 2

Video 3

更重要的是,尝试使用类方法来构造代码,然后将所有类方法放在一个主要的测试方法中。

import unittest
import sqlite3

class MyOrderedTest(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.create_db()
        cls.setup_draft()
        cls.draft_one()
        cls.draft_two()
        cls.draft_three()

    @classmethod
    def create_db(cls):
        cls.conn = sqlite3.connect(":memory:")

    @classmethod
    def setup_draft(cls):
        cls.conn.execute("CREATE TABLE players ('draftid' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 'first', 'last')")

    @classmethod
    def draft_one(cls):
        player = ("Hakeem", "Olajuwon")
        cls.conn.execute("INSERT INTO players (first, last) VALUES (?, ?)", player)

    @classmethod
    def draft_two(cls):
        player = ("Sam", "Bowie")
        cls.conn.execute("INSERT INTO players (first, last) VALUES (?, ?)", player)

    @classmethod
    def draft_three(cls):
        player = ("Michael", "Jordan")
        cls.conn.execute("INSERT INTO players (first, last) VALUES (?, ?)", player)

    def test_unordered_one(self):
        cur = self.conn.execute("SELECT * from players")
        draft = [(1, u'Hakeem', u'Olajuwon'), (2, u'Sam', u'Bowie'), (3, u'Michael', u'Jordan')]
        query = cur.fetchall()
        print query
        self.assertListEqual(query, draft)

    def test_unordered_two(self):
        cur = self.conn.execute("SELECT first, last FROM players WHERE draftid=3")
        result = cur.fetchone()
        third = " ".join(result)
        print third
        self.assertEqual(third, "Michael Jordan")

答案 5 :(得分:7)

不要依赖订单。如果他们使用某些常见状态(如文件系统或数据库),那么您应该创建setUptearDown方法,使您的环境进入可测试状态,然后在测试运行后进行清理。每个测试都应该假设环境是setUp中定义的,并且不应该做出进一步的假设。

答案 6 :(得分:6)

好的,可能会稍晚一点,但无论如何......

您应该尝试使用proboscis库。它将允许您进行测试顺序以及设置任何测试依赖项。我用它,这个库真的很棒。

例如,如果来自 test case #1 module A 依赖于来自 test case #3 > module B CAN 使用该库设置此行为。

答案 7 :(得分:6)

确定测试优先级的原因有很多,其中最重要的是生产力,这正是JUnit Max的目标。在自己的模块中保持非常慢的测试有时很有帮助,这样您就可以从那些没有遭受相同重度依赖性的测试中获得快速反馈。订购也有助于跟踪非完全自包含的测试中的故障。

答案 8 :(得分:4)

在某些情况下,订单可能很重要,并且setUp和Teardown的范围有限。只有一个setUp和tearDown方法,这是合乎逻辑的,但你只能在它们中放入太多信息,直到不清楚setUp或tearDown可能实际上在做什么。

以此集成测试为例:

  

您正在编写测试以查看注册表单和登录表单是否正常工作。在这种情况下,订单很重要,因为没有现有帐户就无法登录。   更重要的是,测试的顺序代表了某种用户交互。每个测试可能代表您正在测试的整个过程或流程中的一个步骤。

将代码划分为逻辑部分有几个优点。

可能不是最好的解决方案但是,我经常使用一种开始实际测试的方法。

def test_registration_login_flow(self):
    _test_registration_flow()
    _test_login_flow()

答案 9 :(得分:3)

一种简单的订购方法" unittest"测试是遵循给出数字名称的init.d机制:

def test_00_createEmptyObject(self):
    obj = MyObject()
    self.assertIsEqual(obj.property1, 0)
    self.assertIsEqual(obj.dict1, {})

def test_01_createObject(self):
    obj = MyObject(property1="hello", dict1={"pizza":"pepperoni"})
    self.assertIsEqual(obj.property1, "hello")
    self.assertIsDictEqual(obj.dict1, {"pizza":"pepperoni"})

def test_10_reverseProperty(self):
    obj = MyObject(property1="world")
    obj.reverseProperty1()
    self.assertIsEqual(obj.property1, "dlrow")

但是,在这种情况下,您可能需要考虑以不同方式构建测试,以便可以构建以前的构造案例。例如,在上面,有一个"构造和veirfy"构造对象并验证它的参数分配的函数。

def make_myobject(self, property1, dict1):  # Must be specified by caller
    obj = MyObject(property1=property1, dict1=dict1)
    if property1:
        self.assertEqual(obj.property1, property1)
    else:
        self.assertEqual(obj.property1, 0)
    if dict1:
        self.assertDictEqual(obj.dict1, dict1)
    else:
        self.assertEqual(obj.dict1, {})
    return obj

def test_00_createEmptyObject(self):
    obj = self.make_object(None, None)

def test_01_createObject(self):
    obj = self.make_object("hello", {"pizza":"pepperoni"})

def test_10_reverseProperty(self):
    obj = self.make_object("world", None)
    obj.reverseProperty()
    self.assertEqual(obj.property1, "dlrow")

答案 10 :(得分:2)

我同意一条毯子“不要做”#34;答案是一个不好的回应。

我有类似的情况,我有一个数据源,一个测试将擦除数据集导致其他测试失败。

我的解决方案是在Bamboo服务器中使用操作系统环境变量...

(1)"数据清除的测试"功能以while循环开始,该循环检查环境变量的状态" BLOCK_DATA_PURGE。"如果" BLOCK_DATA_PURGE"变量大于零,循环将写入一个日志条目,使其处于休眠1秒的效果。一旦" BLOCK_DATA_PURGE"如果值为零,则执行继续测试清除功能。

(2)任何需要表中数据的单元测试都会增加" BLOCK_DATA_PURGE"在开头(在setup()中)并在teardown()中递减相同的变量。

这样做的结果是允许各种数据使用者阻止清除功能,只要他们需要,而不必担心清除可以在测试之间执行。有效地,清除操作被推送到最后一步......或者至少是需要原始数据集的最后一步。

今天我将扩展它以添加更多功能,以允许对REQUIRE_DATA_PURGE进行一些测试。这些将有效地反转上述过程,以确保这些测试仅在数据清除后执行以测试数据恢复。

答案 11 :(得分:1)

请参阅http://docs.python.org/library/unittest.html#organizing-test-codeWidgetTestCase的示例,它说

  

类实例现在将运行其中一个test _ *()方法,并为每个实例单独创建和销毁self.widget。

因此,如果您不访问全局变量,那么指定测试用例的顺序可能毫无用处。

答案 12 :(得分:1)

我为nosedep实现了插件Nose,增加了对测试依赖项和测试优先级的支持。

正如其他答案/评论中提到的那样,这通常是一个坏主意,但是在某些情况下您可能想要这样做(在我的情况下,这是集成测试的性能-进入可测试的系统会产生巨大的开销状态,分钟数还是小时数。)

一个最小的例子是:

def test_a:
  pass

@depends(before=test_a)
def test_b:
  pass

为确保始终在test_b之前运行test_a

答案 13 :(得分:1)

这是一种更简单的方法,具有以下优点:

  • 无需创建自定义TestCase类。
  • 无需装饰每种测试方法。
  • 使用unittest标准负载测试协议。 See the Python docs here.

想法是遍历赋予测试加载程序协议的测试套件的所有测试用例,并创建一个新套件,但按行号对测试进行排序。

代码如下:

import unittest

def load_ordered_tests(loader, standard_tests, pattern):
    """
    Test loader that keeps the tests in the order they were declared in the class.
    """
    ordered_cases = []
    for test_suite in standard_tests:
        ordered = []
        for test_case in test_suite:
            test_case_type = type(test_case)
            method_name = test_case._testMethodName
            testMethod = getattr(test_case, method_name)
            line = testMethod.__code__.co_firstlineno
            ordered.append( (line, test_case_type, method_name) )
        ordered.sort()
        for line, case_type, name in ordered:
            ordered_cases.append(case_type(name))
    return unittest.TestSuite(ordered_cases)

您可以将其放入名为order_tests的模块中,然后在每个unittest Python文件中,声明测试加载器,如下所示:

from order_tests import load_ordered_tests

# This orders the tests to be run in the order they were declared.
# It uses the unittest load_tests protocol.
load_tests = load_ordered_tests

注意:通常建议的将测试分类器设置为None的技术不再起作用,因为Python现在对dir()的输出进行排序,而unittest使用dir()查找测试。因此,即使您没有排序方法,它们仍然可以通过Python本身进行排序!

答案 14 :(得分:1)

似乎它们按测试名称按字母顺序执行(使用字符串之间的比较功能)

由于模块中的测试也仅在以“ test”开头的情况下才执行,所以我要做的是给测试排序以数字:

class LoginTest(unittest.TestCase):
    def setUp(self):
        driver.get("http://localhost:2200")

    def tearDown(self):
        # self.driver.close()
        pass
    def test1_check_at_right_page(self):
        ...
        assert "Valor" in driver.page_source

    def test2_login_a_manager(self):
        ...
        submit_button.click()
        assert "Home" in driver.title

    def test3_is_manager(self):
        ...

请注意,数字不一定是字母顺序-例如,在python shell中,“ 9”>“ 10”为True。考虑使用固定填充为0的十进制字符串(这将避免上述问题),例如“ 000”,“ 001”,...“ 010” ...“ 099”,“ 100”,...“ 999”。

答案 15 :(得分:0)

单元测试背后的理念是使它们彼此独立。这意味着每个测试的第一步始终是尝试重新思考如何测试每个部分以符合该理念。这可能涉及通过将测试范围缩小到较小的范围来改变测试方法和创造性。

但是,如果您仍然发现需要按特定顺序进行测试(因为这是可行的),您可以尝试查看Python unittest.TestCase execution order的答案。

答案 16 :(得分:0)

要随机化测试方法的顺序,可以猴子修补unittest.TestLoader.sortTestMethodsUsing属性

if __name__ == '__main__':
    import random
    unittest.TestLoader.sortTestMethodsUsing = lambda self, a, b: random.choice([1, 0, -1])
    unittest.main()

可以使用相同的方法来执行所需的任何命令。

答案 17 :(得分:-1)

与此处所说的相反: - 测试必须孤立运行(顺序一定不重要) 和 - 对它们进行排序非常重要,因为它们描述了系统的功能以及开发人员如何实现它。

IOW,每个测试都会为您提供系统信息和开发者逻辑。

因此,如果这些信息没有排序,可能会使您的代码难以理解。