2009-09-10 68 views
4

我正在使用Python内置的unittest模块,我想编写一些不重要的测试。非关键单元测试失败

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

例如,我的程序设计为使用自定义类型“A”。如果它不能与“A”一起工作,那么它就会被破坏。但是,为了方便起见,它大部分还应该与另一种类型“B”一起工作,但这不是强制性的。如果它不能与“B”一起工作,那么它不会被破坏(因为它仍然与“A”一起工作,这是它的主要目的)。如果不能与“B”合作并不重要,我只会错过我可能拥有的“奖励功能”。

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

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

回答

7

作为一个实际问题,我可能会使用print语句来指示在这种情况下失败。更正确的解决方案是使用警告:

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

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

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

编辑:

我们在Django处理这个问题的方式是,我们有一些测试,我们希望失败,我们有其他基于环境,我们跳过。由于我们通常可以预测一个测试是否应该失败或通过(即如果我们不能导入某个模块,系统没有它,所以测试不起作用),我们可以智能地忽略失败的测试。这意味着我们仍然运行每一个将通过的测试,并且没有“可能”通过的测试。单元测试在可预测的情况下是非常有用的,并且能够在运行之前检测测试是否应该通过,这使得这成为可能。

+0

你和Guilherme Chapiewski都建议单元测试不适合这份工作。 日志记录似乎是一种矫枉过正,但也许警告可能“足够好”和“足够简单”。稍后再试验它们。 – 2009-09-10 17:52:48

+0

我不知道。没有一个警告看起来像是合适的。我的意思是,UserWarning,但这符合一切。这是另一个进口,另一个依赖,另一个要打破。此外,警告是在事情“几乎破裂”的时候。我想你可以测试B,如果它失败了,抛出“几乎”的错误,但我觉得这可能不是警告模块的目的。 – Jonathanb 2009-09-10 21:20:48

4

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

我的建议是为你的应用程序做,或不要,没有尝试。

+0

+1做或不做,永远不要尝试。 – 2009-09-10 17:56:19

-1

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

如果您的程序必须与类型A一起工作,那么如果失败,则测试失败。如果不需要与B一起工作,做这样的测试有什么价值?

+0

做这样的测试的价值是,如果修复B所需的工作量最小,我现在可以修复它(或稍后,更多时间)。另外,如果稍后的重构突然“修复”B,很高兴知道。 – 2009-09-10 17:42:34

4

单元测试中的声明是二进制的:他们会工作,否则他们会失败,没有中期。因此,为了创建这些“非关键”测试,当不希望测试失败时,不应使用断言。你应该小心这样做,这样你就不会损害测试的“实用性”。

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

+0

尽管“assert_rate> 8.5”看起来不错,但这样的解决方案隐藏了实际的success_rate。 – 2009-09-10 17:49:21

+0

但单元测试通常不涉及具体细节(无论如何,在测试过程中将细节输出到文本文件都很简单)。单元测试是黑/白,通过/失败的事情。他们可以做到这一点,因为每个测试都非常精细,不一定是灰色的,它们可以是黑色/白色,当你缩小时会变灰。 – Jonathanb 2009-09-10 20:45:32

1

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

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

另一种方式来处理,这是分离出的考验,只能运行他们得到合格/不合格报告,并没有任何的构建依赖关系(这取决于您的构建设置)。

0

看看鼻子:http://somethingaboutorange.com/mrl/projects/nose/0.11.1/

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

+0

对不起,但建议使用另一个测试框架,而不是解释它将如何解决我的问题(或者即使它会解决我的问题),它不是很有帮助。 – 2009-09-14 12:50:51

3

unittest文档您链接:

相反unittest.main(),有 其他的方式来运行与控制的 更精细的水平,少简洁 输出的测试,并没有要求的从命令行运行 。例如, 最后两行可更换 有:

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

在你的情况,你可以创建具临界和非关键测试单独TestSuite实例。您可以使用命令行参数控制将哪个套件传递给测试运行器。测试套件还可以包含其他测试套件,以便您可以创建大层次结构。

+0

不错!这几乎是我要做的警告/日志/打印,但你的方式是更清洁!我喜欢它,很快就要测试它! – 2009-09-10 22:52:20

0

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

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

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

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有用。也许我不会使用它这个简单的个人项目,但它可能对未来的项目有用。

+0

我不确定是否将自己的问题的答案标记为“最佳答案”在堆栈溢出时声音很好...... – 2009-09-14 19:52:13

+0

是的,没关系,没有问题。实际上,昨天我花了大量的时间在Meta Stack Overflow上搜索这个。请参阅:http://meta.stackexchange.com/questions/14306/my-improved-answer-based-on-anothers-accepted-answer-for-my-own-question和http://meta.stackexchange.com/ questions/13427/suggestion-for-how-you-could-handle-multiple-helpful-answers and http://meta.stackexchange.com/questions/13413/how-do-i-combine-two-answers-to-创建最最佳的答案 - 上计算器/ 13418#13418 – 2009-09-15 10:09:42