Unittest测试订单

nmb*_*ten 43 python unit-testing

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

class TestFoo(TestCase):
    def test_1(self):
        ...
    def test_2(self):
        ...
Run Code Online (Sandbox Code Playgroud)

要么

class TestFoo(TestCase):
    def test_a(self):
        ...
    def test_b(self):
        ...
Run Code Online (Sandbox Code Playgroud)

小智 73

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

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

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

  • 单元测试应该是独立的,真实的.或者更好地说,它们应该能够独立运行,原因很多.但是,我也使用unittest框架编写功能测试,集成测试和系统测试,并且由于集成测试中的系统状态MATTERS,因此在不对它们进行排序的情况下运行是不可行的! (15认同)
  • 我同意你的评论,如果没有解释就没有帮助"不要那样做"的评论,但是我已经说过*有*真正的理由为什么在测试之间存在依赖关系并不是一个好主意.其中最主要的是让测试失败是件好事,因为特定的事情已经破裂,而不是因为你正在运行的测试与其他一些你不测试的测试之间存在一些不明确的,未记录的链接.如果你从未运行过孤立的测试,那么这很好,但能够运行单独的测试在某些情况下是有帮助的,并且这在彼此依赖的情况下是不可能的. (5认同)
  • 答案是单元测试应该彼此独立,以便您可以单独运行和调试它们。 (2认同)
  • 您能提供一个如何设置测试执行顺序的示例吗? (2认同)

max*_*max 68

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

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

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

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

  • +1为此:"如果你正在测试状态怎么办".例如,在测试与数据库后端通信的方法时,经常会发生这种情况.不要是教条主义的,除了使每个单元测试被隔离的合理规则之外,还有合法的例外. (23认同)
  • 可能有人会说,测试对象的状态应该涉及模拟所述对象。当然,这是假设有合适的模拟库可用。此外,模拟需要更多时间,并且可能需要多层模拟框架。它应该只是一个指导方针。在某种程度上,模拟到第 n 级会变得比仅仅测试一个对象中的单一状态所需要的更加复杂。FWIW,我原则上同意@max。 (2认同)
  • ```sortTestMethodsUsing = None``` 似乎在 Python 3.9.5 中不起作用,实际上文档不再提及这一点。但是,``sortTestMethodsUsing = lambda *args: -1``` 具有按测试在每个模块中定义的顺序对测试进行排序的效果。如果你没有对自定义加载器做任何花哨的事情```unittest.defaultTestLoader.sortTestMethodsUsing = lambda *args: -1```对我来说工作得很好。 (2认同)

zou*_*oul 12

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

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

  • 每个测试都是前一个测试的延续.这是测试顺序的简单示例.测试用户订阅,测试禁用订阅,测试取消订阅订阅和禁用订阅.如果没有订购测试,我必须再次在之前的测试中测试所有测试的东西.这是错误的方式吗? (19认同)
  • 我能想到的一个原因是两个测试不依赖于彼此,而是他们正在测试的组件.想象一下测试一个B类的子类,如果A有问题,它也会失败B测试.首先得到与A测试相关的错误会更好.但总的来说,它确实不应该有很大的不同. (6认同)
  • 对于调试,将(独立的)测试从简单到复杂排序是很有意义的. (6认同)
  • 如果访问数据库可能很难并行运行(django大多是这种情况) (2认同)
  • @MitchellModel Django使用事务在测试之间回滚对数据库的更改.您的第二个测试不应该看到对第一个测试中创建的数据库的修改.(如果您是,您的视图可能正在使用事务 - 您应该使用Django的TransactionTestCase而不是TestCase用于该视图) (2认同)
  • 如果您因代码更改而破坏了 2 或 3 个测试,您会注意到第一个失败的测试并尝试修复它。之后,您将再次运行所有测试。如果您这次在不同的测试中测试失败,那么您的修复似乎对第一个测试有效,而实际上您的更改可能没有做任何事情,但unittest只是以不同的顺序运行测试。 (2认同)
  • 如果您可以指定每个测试的依赖关系不是很好吗?我同意每个测试彼此完全独立通常会更好。但有时在处理数据库或复杂的设置时,引入基本依赖关系会更简单、更有效(即测试运行得更快)。 (2认同)
  • 根据我的经验,无论这种答案一开始感觉多么不方便,情况往往都是如此。不过,请随意不同意。 (2认同)

Elm*_*der 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)
Run Code Online (Sandbox Code Playgroud)

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

test_it.test_aaa ... ok
test_it.test_zzz ... ok
test_it.test_bbb ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.050s
OK
Run Code Online (Sandbox Code Playgroud)

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

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

  • 我有一套包含几百个测试用例的套件,遗憾的是我不能说这是真的。也不是故意回避的,有的时候确实就是这个顺序。另外,我不确定它是否可以在鼻子的某个地方进行配置,但是滚动浏览帮助我也无法找出该选项。 (2认同)

Jas*_*rth 8

我半同意不能订购测试的想法.在某些情况下,它有助于(它更容易该死!)将它们按顺序排列......毕竟这就是UnitTest中"单位"的原因.

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

如果您之前没有使用过Mock,那么这里有一些YouTube视频.

视频1

视频2

视频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")
Run Code Online (Sandbox Code Playgroud)


kfs*_*one 8

排序“单元测试”测试的一个简单方法是遵循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")
Run Code Online (Sandbox Code Playgroud)

但是,在这种情况下,您可能需要考虑以不同的方式构建测试,以便可以在以前的构造案例的基础上进行构建。例如,在上面,使用“构造和验证”函数来构造对象并验证其参数分配可能是有意义的。

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")
Run Code Online (Sandbox Code Playgroud)


小智 7

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


小智 7

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

这个想法是遍历提供给测试加载器协议的测试套件的所有测试用例并创建一个新套件,但测试按行号排序。

这是代码:

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)
Run Code Online (Sandbox Code Playgroud)

您可以将其放入名为 order_tests 的模块中,然后在每个单元测试 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
Run Code Online (Sandbox Code Playgroud)

注意:经常建议的将测试排序器设置为 None 的技术不再有效,因为 Python 现在对 dir() 的输出进行排序,而 unittest 使用 dir() 来查找测试。所以即使你没有排序方法,它们仍然会被Python本身排序!


era*_*man 6

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

  • 对不起,但我倾向于不同意.单元测试不应该相互依赖,但如果它们按照指定的顺序执行,它仍然经常有意义.比如,你有两个函数``a``和``b``和``b``使用``a``.那么如果在``test_b``之前执行``test_a``会好得多,因为如果``a``包含错误,你会发现这么早,而不是试图找到``b中的错误``. (3认同)

gah*_*cep 6

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

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

例如,如果test case #1module A应取决于test case #3module B的CAN使用图书馆的这种行为.


mor*_*sik 6

来自unittest — 单元测试框架

请注意,运行各种测试用例的顺序是通过根据字符串的内置顺序对测试函数名称进行排序来确定的。

如果您需要明确设置顺序,请使用整体测试。

class Monolithic(TestCase):
  def step1(self):
      ...

  def step2(self):
      ...

  def steps(self):
    for name in sorted(dir(self)):
      if name.startswith("step"):
        yield name, getattr(self, name)

  def test_steps(self):
    for name, step in self.steps():
      try:
        step()
      except Exception as e:
        self.fail("{} failed ({}: {})".format(step, type(e), e)
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请查看此堆栈溢出问题


Jon*_*gat 5

在某些情况下,顺序可能很重要,而 setUp 和 Teardown 的作用又太有限。只有一种 setUp 和tearDown 方法,这是合乎逻辑的,但是您只能在其中放入如此多的信息,直到不清楚setUp 或tearDown 实际在做什么为止。

以本次集成测试为例:

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

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

这可能不是最好的解决方案,但我经常使用一种方法来启动实际测试:

def test_registration_login_flow(self):
    _test_registration_flow()
    _test_login_flow()
Run Code Online (Sandbox Code Playgroud)