单元测试和抽象测试

而不是加入


Unittest可能是Python中最著名的测试编写框架。 它非常容易学习,也很容易在您的项目中开始使用。 但是没有什么是完美的。 在本文中,我想谈一谈我个人(我认为不是一个人)在单元测试中缺少的一项功能。

关于单元测试的一点


在讨论(并谴责)测试框架之前,我认为有必要对测试进行一些讨论。 当我第一次听到“单元测试”一词时,我认为某些质量控制服务负责检查软件模块是否符合要求。 当我发现这些相同的测试应该由程序员编写时,请想象一下我的惊讶。 起初我根本没有编写测试……。 当您早上醒来并向用户阅读时,这些感觉都无法替代这些感觉。 迫切需要做些事情。” 最初,在我看来这是一个完全正常的过程,直到我编写了第一个单元测试后才发现错误。 在发现问题之前,单元测试通常似乎毫无用处。 现在,我不能在没有编写测试的情况下提交新功能。
但是测试不仅用于验证代码是否正确编写。 我认为这是执行测试的功能列表:

  • 检测程序代码中的错误
  • 使程序员相信他们的代码有效
  • 上一段的推论:安全修改程序的能力
  • 测试-一种最能正确描述系统行为的文档

从某种意义上说,测试重复了程序的结构。 构建程序的原理是否适用于测试? 我相信是的-这是相同的程序,尽管要测试另一个。

问题描述


在某个时候,我有了编写抽象测试的想法。 那是什么意思 这是一个不会自行执行的测试,而是声明依赖于继承程序中定义的参数的方法。 然后我发现我无法在单元测试中做到这一点。 这是一个例子:

class SerializerChecker(TestCase): model = None serializer = None def test_fields_creation(self): props = TestObjectFactory.get_properties_for_model(self.model) obj = TestObjectFactory.create_test_object_for_model(self.model) serialized = self.serializer(obj) self.check_dict(serialized.data, props) 

我认为,即使不知道TestObjectFactory的实现和check_dict方法的实现,很明显props是对象的属性字典,而obj是我们要检查序列化器的对象。 check_dict递归地检查字典是否匹配。 我认为许多熟悉unittest的人会立即说该测试不符合我对abstract的定义。 怎么了 因为test_fields_creation方法将从此类执行,所以我们绝对不需要。 经过一些信息搜索,我得出的结论是,最适当的选择不是继承TestCase的SerializerChecker,而是以某种方式实现继承人:

 class VehicleSerializerTest(SerializerChecker, RecursiveTestCase): model = Vehicle serializer = VehicleSerialize 

RecursiveTestCase是TestCase的后代,它实现了check_dict方法。

这个解决方案在几个方面都很难做到:

  • 在SerializerChecker类中,我们需要知道子级必须继承自TestCase。 对于那些不熟悉此代码的人,这种依赖性可能会导致问题。
  • 开发环境强地认为我错了,因为SerializerChecker没有check_dict方法

图片

开发环境抛出的错误

看来您可以简单地为check_dict添加一个存根,并且所有问题都已解决:

 class SerializerChecker: model = None serializer = None def check_dict(self, data, props): raise NotImplementedError def test_fields_creation(self): props = TestObjectFactory.get_properties_for_model(self.model) obj = TestObjectFactory.create_test_object_for_model(self.model) serialized = self.serializer(obj) self.check_dict(serialized.data, props) 

但这不是该问题的完整解决方案:

  • 实际上,我们创建了一个接口,该接口不是实现此类的后代,而是实现了RecursiveTestCase,它为体系结构创建了合理的问题。
  • TestCase有许多assert *方法。 我们真的需要为每个使用过的存根编写一个存根吗? 还是一个不错的解决方案?

总结一下


Unittest没有提供“断开”从TestCase继承的类的理智的能力。 如果将这样的功能添加到框架中,我将非常高兴。 您如何解决这个问题?

Source: https://habr.com/ru/post/zh-CN447438/


All Articles