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循环是"更干净",但在不同行上使用不同值的复制粘贴可提供有用的堆栈跟踪.
如果通过清洁器意味着更少的代码,那么这种更干净的代码并不总是更易读的代码.事实上,它通常不太可读(特别是当你回到它时).您可以随时使用花哨的重构,但您需要知道何时停止.从长远来看,使用更明显,更简单的代码总是比尝试减少少量代码以获得人工增益更好- 而不仅仅是单元测试.
单元测试符合他们自己的规则.例如,它们通常允许不同于常规代码标准所说的命名约定,您几乎不会记录它们 - 它们是您的代码库的特殊部分.此外,重复代码并不常见.实际上,通常会有许多类似的小型测试.
目前,即使在编写测试阶段,您的测试仍然令人困惑 - 想象一下从现在起3个月后回到代码中.想象一下,由于其他人在其他地方做了一些改变,其中一项测试破坏了.它不会变得更好.
以这样的方式设计您的测试,当其中一个中断时,您立即知道它为什么这样做以及在哪里.不仅如此 - 以这样的方式设计它们,你可以在眨眼间告诉他们正在做什么.在测试代码中有for循环,ifs和基本上任何其他类型的控制流机制(或过于广泛的重构),通常会导致一个问题浮现在你的脑海中 - 我们在这里做什么?这是你不想发现自己要问的那种问题.
总结一下这篇冗长的帖子,用比我更聪明的人的话来说:
任何傻瓜都可以编写计算机可以理解的代码.优秀的程序员编写人类可以理解的代码.
-Martin Fowler等,Refactoring:Improved the Design of Existing Code,1999
帮自己一个忙,坚持这个规则.