Python Unittest模块化与可读性

NoB*_*ugs 7 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.
Run Code Online (Sandbox Code Playgroud)

虽然它是模块化的,但我注意到任何失败都会产生类似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))
Run Code Online (Sandbox Code Playgroud)

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

k.m*_*k.m 7

如果通过清洁器意味着更少的代码,那么这种更干净的代码并不总是更易读的代码.事实上,它通常不太可读(特别是当你回到它时).您可以随时使用花哨的重构,但您需要知道何时停止.从长远来看,使用更明显,更简单的代码总是比尝试减少少量代码以获得人工增益更好- 而不仅仅是单元测试.

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

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

目前,即使在编写测试阶段,您的测试仍然令人困惑 - 想象一下从现在起3个月后回到代码中.想象一下,由于其他人其他地方做了一些改变,其中一项测试破坏了.它不会变得更好.

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

总结一下这篇冗长的帖子,用比我更聪明的人的话来说:

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

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

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