Python Unittest模块化与可读性

时间:2012-02-14 04:58:16

标签: python unit-testing assert

我有一个Python单元测试,其中一些测试具有相同的类型对象测试。一个测试类的基本概要是:

class TestClass(unittest.TestCase):
    def setup(self):
        ...

    def checkObjects(self, obj):
        for i in [...values...]:
            self.assertEqual(starttags(i,obj), endtags(i,obj))

    def testOne(self):
        #Get object one.
        checkObjects(objone)

    def testAnother(self):
        #Access another object.
        checkObjects(another)

    ... various tests for similar objects.

虽然它是模块化的,但我注意到任何失败都会产生类似AssertionError的错误:number!= anothernumber,以及产生错误的代码行self.assertEqual(starttags(i,obj), endtags(i,obj))。如果我列出了测试而不是放置for循环,我会有类似的东西:

self.assertEqual(starttags(value1,obj), endtags(value1,obj))
self.assertEqual(starttags(value2,obj), endtags(value2,obj))

其中显示哪种情况确实导致错误,但是复制粘贴代码,我认为通常不推荐使用。我最近发现了一个问题,当一个贡献者重新编写了一个更 clean 单元测试时,遗憾的是它会在断言失败时提供很少的调试信息。那么,这些案例中最好的做法是什么?类似于元组列表的东西,使用assertEquals进入for循环是“更干净”,但在不同行上使用不同值的复制粘贴可以提供有用的堆栈跟踪。

2 个答案:

答案 0 :(得分:7)

如果通过 cleaner 意味着更少的代码,那么这种 clean 代码并不总是更多可读代码。事实上,它通常不太可读(特别是当你回到它时)。您可以随时使用花哨的重构,但您需要知道何时停止。从长远来看,使用更明显,更简单的代码总是比尝试挤出少一行代码以获得人工增益更好 - 而不仅仅是单元测试。 / p>

单元测试符合他们自己的规则。例如,它们通常允许不同于常规代码标准所说的命名约定,您几乎不会记录它们 - 它们是您的代码库的特殊部分。此外,重复代码并不常见。实际上,通常会有许多类似的小型测试。

设计您的测试(代码)时要考虑到简单性

目前,即使在编写测试阶段,您的测试仍然令人困惑 - 想象一下从现在开始的3个月后回到该代码。想象一下,由于别人某些其他地方进行某些更改而导致其中一项测试失败。它不会变得更好。

以这样的方式设计您的测试,当其中一个测试中断时,您立即知道为什么这样做以及在哪里。不仅如此 - 以这样的方式设计它们,你可以在眨眼间告诉他们正在做什么。在测试代​​码中有 for循环 ifs 以及基本上任何其他类型的控制流机制(或过于广泛的重构),通常会导致一个问题引起你的注意 - 我们在这做什么? 这是你不想发现的问题。

用比我更聪明的人的话总结这篇冗长的帖子:

  

任何傻瓜都可以编写计算机可以理解的代码。优秀的程序员编写人类可以理解的代码。

     

-Martin Fowler等,Refactoring: Improving the Design of Existing Code, 1999

帮自己一个忙,坚持这条规则。

答案 1 :(得分:0)

使用nosetests,它可以让你的测试变得更加清洁:

#!/usr/bin/python
def test_one():
    for i in [...]:
        assert xxx(i) == yyy

def test_two():
    ...