非关键单元测试失败

时间:2009-09-10 17:11:51

标签: python unit-testing

我正在使用Python的内置unittest模块,我想编写一些不重要的测试。

我的意思是,如果我的程序通过了这样的测试,那太好了!但是,如果它没有通过,那不是一个真正的问题,程序仍然可以工作。

例如,我的程序旨在使用自定义类型“A”。如果它无法使用“A”,那么它就会被破坏。但是,为方便起见,大多数也应该使用另一种类型“B”,但这不是强制性的。如果它不能与“B”一起使用,那么它就不会被破坏(因为它仍然适用于“A”,这是它的主要目的)。不能使用“B”并不重要,我会错过一个我可以拥有的“奖励功能”。

另一个(假设的)示例是编写OCR时。该算法应该识别来自测试的大多数图像,但是如果其中一些图像失败则可以。 (不,我不是在写OCR)

有没有办法在unittest(或其他测试框架)中编写非关键测试?

11 个答案:

答案 0 :(得分:8)

实际上,在这种情况下,我可能会使用print语句来表示失败。更正确的解决方案是使用警告:

http://docs.python.org/library/warnings.html

但是,您可以使用日志工具生成更详细的测试结果记录(即设置“B”类失败以向日志写入警告)。

http://docs.python.org/library/logging.html

编辑:

我们在Django中处理这个问题的方式是我们有一些我们希望失败的测试,而我们还有其他一些我们根据环境跳过的测试。由于我们通常可以预测测试是否应该失败或通过(即如果我们无法导入某个模块,系统没有它,因此测试将无法工作),我们可以智能地跳过测试失败的测试。这意味着我们仍然会运行每个将通过的测试,并且没有“可能”通过的测试。当单元测试以可预测的方式执行时,单元测试最有用,并且能够在运行之前检测测试是否应该通过,这使得这成为可能。

答案 1 :(得分:4)

单元测试中的断言是二进制:它们会起作用或者它们会失败,没有中期。

鉴于此,要创建那些“非关键”测试,当您不希望测试失败时,不应使用断言。您应该仔细执行此操作,以免损害测试的“有用性”。

我对你的OCR示例的建议是,你使用一些东西记录测试代码中的成功率,然后创建一个断言,如:“assert success_rate> 8.5”,这应该给你想要的效果。

答案 2 :(得分:3)

我不完全确定unittest的工作原理,但大多数单元测试框架都有类似于类别的东西。我想你可以对这些测试进行分类,将它们标记为被忽略,然后只有当你对它们感兴趣时再运行它们。但我从经验中知道,忽略测试很快就会变成......只是忽略了没有人会运行的测试,因此浪费时间和精力来编写它们。

我的建议是让你的应用做或不做,没有尝试。

答案 3 :(得分:3)

来自您链接的unittest文档:

  

而不是unittest.main(),有   用a运行测试的其他方法   更精细的控制,更少的简洁   输出,无需运行   从命令行。例如,   最后两行可以替换   用:

suite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)
unittest.TextTestRunner(verbosity=2).run(suite)

在您的情况下,您可以为批判性和非关键性测试创建单独的TestSuite个实例。您可以使用命令行参数控制将哪个套件传递给测试运行器。测试套件还可以包含其他测试套件,以便您可以根据需要创建大型层次结构。

答案 4 :(得分:2)

谢谢你的答案。不仅一个答案真的完整,所以我在这里写a combination of all answers that helped me。如果你喜欢这个答案,请投票给负责此事的人。

<强>结论

单元测试(或至少unittest模块中的单元测试)是二进制的。正如Guilherme Chapiewski says他们会工作或他们会失败,没有中期。

因此,我的结论是单元测试并不是这项工作的正确工具。似乎单元测试更关心“保持一切正常,没有预期失败”,因此我不能(或者说不容易)进行非二进制测试。

因此,如果我正在尝试改进算法或实现,单元测试似乎不是正确的工具,因为单元测试无法告诉我一个版本与另一个版本相比有多好(假设它们都是正确实现,然后两者都将通过所有单元测试。)

我的最终解决方案

我的最终解决方案基于ryber's ideawcoenen answer中显示的代码。我基本上扩展了默认TextTestRunner并使其更简洁。然后,我的主代码调用两个测试套件:使用标准TextTestRunner的关键测试套件和使用我自己的简洁版本的非关键套件。

class _TerseTextTestResult(unittest._TextTestResult):
    def printErrorList(self, flavour, errors):
        for test, err in errors:
            #self.stream.writeln(self.separator1)
            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
            #self.stream.writeln(self.separator2)
            #self.stream.writeln("%s" % err)


class TerseTextTestRunner(unittest.TextTestRunner):
    def _makeResult(self):
        return _TerseTextTestResult(self.stream, self.descriptions, self.verbosity)


if __name__ == '__main__':
    sys.stderr.write("Running non-critical tests:\n")
    non_critical_suite = unittest.TestLoader().loadTestsFromTestCase(TestSomethingNonCritical)
    TerseTextTestRunner(verbosity=1).run(non_critical_suite)

    sys.stderr.write("\n")

    sys.stderr.write("Running CRITICAL tests:\n")
    suite = unittest.TestLoader().loadTestsFromTestCase(TestEverythingImportant)
    unittest.TextTestRunner(verbosity=1).run(suite)

可能的改进

知道是否存在任何非二进制测试的测试框架(如Kathy Van Stone suggested)仍然有用。可能我不会使用这个简单的个人项目,但它可能对未来的项目有用。

答案 5 :(得分:2)

Python 2.7(和3.1)增加了对跳过某些测试方法或测试用例的支持,并将某些测试标记为预期失败

http://docs.python.org/library/unittest.html#skipping-tests-and-expected-failures

标记为预期失败的测试不会被视为TestResult上的失败。

答案 6 :(得分:1)

有一些测试系统允许警告而不是失败,但test_unit不是其中之一(我不知道哪些操作,但是,除非你想扩展它(这是可能的)。

您可以进行测试,以便他们记录警告而不是失败。

另一种处理此问题的方法是分离测试并仅运行它们以获取通过/失败报告,而不具有任何构建依赖性(这取决于您的构建设置)。

答案 7 :(得分:0)

看看Nose:http://somethingaboutorange.com/mrl/projects/nose/0.11.1/

有很多命令行选项可供选择要运行的测试,您可以保留现有的单元测试。

答案 8 :(得分:0)

另一种可能性是创建一个“B”分支(你正在使用某种版本控制,对吗?)并在那里进行单元测试“B”。这样,你保持你的发布版本的单元测试干净(看,所有点!),但仍然有测试B.如果你使用现代版本控制系统,如git或mercurial(我偏爱mercurial),分支/克隆和合并是微不足道的操作,所以这就是我的建议。

但是,我认为你正在使用测试来做他们不应该做的事情。真正的问题是“对你来说,'B'有多重要?”因为您的测试套件应该只在其中进行测试,您关心它们是否通过。测试一旦它们失败,就意味着代码被破坏了。这就是为什么我建议只在“B”分支中测试“B”,因为那将是您开发“B”功能的分支。

如果您愿意,可以使用记录器或打印命令进行测试。但是如果你不太在乎它在单元测试中被标记出来就会被打破,我会严肃地质疑你是否足够关心测试它。此外,这增加了不必要的复杂性(额外的变量来设置调试级别,多个测试向量完全独立,但在同一空间内运行,导致潜在的冲突和错误等)。除非你正在开发一个“Hello,World!”应用程序,我怀疑你的问题集很复杂,没有增加额外的,不必要的复杂性。

答案 9 :(得分:0)

使用子测试:

with self.subTest('description of subtest here'):
   self.assertEqual(non_critical_value, 3)

# lines below will be executed even if the assertion above fails

我很惊讶之前没有提到它。我没有看到很多人在使用它,这是一个很棒的功能。

答案 10 :(得分:-1)

您可以编写测试,以便计算成功率。 使用OCR,您可以抛出代码1000图像并要求95%成功。

如果您的程序必须使用类型A,那么如果失败则测试失败。如果不需要使用B,那么进行这样的测试的价值是什么?