如何让汽车为您编写代码测试

我们生活在一个不完美的世界。人们在这里编写代码,人们自然容易犯错误。一切都会好起来的,错误可以在测试阶段发现,并且不允许伤害任何人。如果编写测试,则是可能的。人们出于某种原因不喜欢做什么。但是也许有希望-从书面代码自动生成测试。

朱莉娅·沃尔科娃(Julia Volkova)希望在现实中测试该想法,并试图将基于代码的测试创建转移到机器上,而无需使用其他指令或合同。朱莉娅(Julia)将向您介绍这一旅程带给元编程,AST,解析和标记化世界的发现,以及所有这些使我们在莫斯科Python Conf ++的自动生成测试中实现的目标。同时,我问这个想法从何而来-自动测试,原型的基础是什么以及尚待完成的工作。

朱莉亚·沃尔科娃(核苷)GridDynamics的高级Python开发人员。闲暇时,他编写宠物项目,有时在现实生活中会找到应用。因此,一遍又一遍地测试遗留代码,Julia注意到许多事情可以自动完成。当然,对于经验丰富的开发人员来说,理解代码并编写“正确的”测试有时太困难了。但是自动化可以很好地进行很多简单的测试并准备代码库,开发人员可以自行决定对其进行修改。

-让我们从患者本人开始,您为什么认为人们不编写测试?聪明的人说要写测试,但他们仍然不写。为什么会有这样的问题?

-我认为有几个原因。首先,我们大多数人本质上都是懒惰的。很少有人直接喜欢写测试-早上醒来就说:“我们必须以15个测试开始新的一天,否则一切都会很糟糕,但同时我的生活也不会成功。” 自然懒惰通常会表现出来,尤其是当您发现该方法不是很有趣时,它具有清晰的原始代码,但是您仍然需要通过测试来覆盖它。
很少编写TDD,因此不仅需要编写测试,还需要花费时间在代码上。
问题是没有无限的时间分配给开发。总有限时的心愿单产品。通常,在产品团队中,昨天一切都是必需的,因为时间就是金钱。在管理人员看来,我们对功能的绿色化程度越高,我们的产品就会越昂贵和越好。而且测试覆盖率,代码质量并不总是直接影响添加功能,代码支持,更新等的后续速度。

我们经常将一切归咎于经理,并说他们没有给我们足够的时间,否则我们会坐下来编写测试。实际上,并非总是如此。并非总是有经验的健壮的开发人员说要编写测试,而年轻的同事则不想这样做。

我从事IT已有很长时间,但是直接从事开发工作已达3-4年。在那之前,我在管理职位上工作更多,并且看到了不同的开发人员。有很多人不能被称为没有经验的人,因为他们已经编写了10年的代码,但同时也认为不需要进行这样的测试。假设您不需要用单元测试来覆盖代码,因为有一个质量检查工程师需要捕获错误。而且他们认为这样的工程师不能通过端到端测试涵盖所有情况。

“如果您不走极端,您怎么看?谁应该编写测试?”是程序员本人,还是初级人员,或者相反,是团队中最酷的开发人员?

-如果我们谈论的是单元测试,则绝对不应进行质量检查。这些绝对应该是在提交之前检查,通过和编写的那些测试,应该直接针对拉取请求,决不能再由其他人稍后编写它们。例如,作为一个懒惰的非初级开发人员,我只是让初级人员编写针对原始代码的测试。对于某些事情,只需简单地阅读中级代码并编写断言就足够了,这些工作非常适合初级人员,并且对他们的发展很有用。

这些单元测试仅按原样覆盖状态代码。这些测试不会检查与任务中的任务要求相关的功能是否有效,而只是确保代码能够执行并正确执行...

但是,要验证代码对业务需求的有效性,对于业务逻辑,必须执行这些需求的人员。他必须了解测试内容和内容。但是,如果一个人最初不了解问题,写出一种方法可以解决不正确的问题,但是对这种不正确的方法进行了正确的测试,则尚不清楚如何解决。

-我们可以说问题是人们对软件开发过程的进展了解甚少?

-这是非常主观的。您将自己想象成一个开发人员的单元,他们了解需要测试,为什么需要测试,并且您认为这是正确的。但是有相当一部分开发人员认为这是多余的。而且,从某种意义上讲,当经理说测试不需要覆盖所有代码时,他们可能以自己的方式正确,仅在阶段进行手动测试就足够了。
说不喜欢测试的人不是熟练的开发人员并不总是正确的。
他有自己的远见,不适合我判断。我仍然经常遇到已经写了10年代码的开发人员,他们说用单元测试覆盖所有内容是多余的,足够的冒烟测试和QA工作就足够了。

反过来,我对没有功能单元测试的项目感到不舒服。对我来说重要的是,至少要有测试来保证免受人为因素的影响,能够捕获随机放置的逗号或字典中更改的键名。但是我不喜欢花时间,因为我一直想做更多的“智能”任务。因此,我正在考虑使测试编写过程自动化的工具。

-您认为Python是动态类型的,并且在编译阶段不检查任何内容吗?用其他语言会更容易吗?

-我认为,发挥力强。这是一个有关类型的永恒故事,但是随着类型注释的出现,它变得更易于使用。

例如,在Python中可能有嵌套的函数链,由于某种原因,列表末尾的期望值将变成字典。执行可能永远不会达到最终功能,但在某些情况下(如果在某些特殊情况下确实如此)会出现错误。

当然,对于类型化的语言,这原则上是不会发生的,因为在编译阶段就已经发生了错误。在这方面,当然,Python提供了其他方式来射击自己的脚(头部和其他部位)。尤其是当您使用分支逻辑处理大型项目时,可以将数据倒入不同的变体和不同的聚合中。

-那么典型的是什么?您认为打字应该是最大还是最小?输入动态代码的平衡应该是什么?

-这又是很主观的。许多人之所以来到Python是因为没有类型,而且它是如此灵活和方便。您不应忘记这一点,也不要淘汰庞大的开发人员层,包括也编写代码的数据科学家和分析师。假设我作为一个后端开发人员,当打字无处不在时,我当然会比较自在。理想情况下,mypy也可以。

但是在我参与的大多数项目中,这是不可能的。因为该项目还有数据分析员,他们说这是因为他们不想用类型来编写Python,所以对他们来说非常方便。
许多人认为,加上Python在没有类型和类型的情况下。
您需要成长到一定水平才能了解何时以及为什么它成为负号。在某些小型Python脚本或小型项目中,我也不使用类型,因为我知道在2函数脚本中,并不是特别需要类型。但这是粗略地说,我很快就屈膝将某物从底座中拉出的东西。在大型项目中,如果没有其他开发人员的抵制,我会尝试将类型添加到最大数量。

-我对此完全同意。剩下的只是要了解如何使用类型,因为这是一个单独的晦涩的话题。

: «, Haskell , : , . Python , , ».

— . , , legacy- smoke-. . ?

-我不会说我的方法更好,只是有所不同。可以的话,用烟雾测试覆盖代码是很好的。我以前的项目是与测试相关的典型痛苦。它是一个包含8个微服务和2万行代码的数据科学平台。问题在于,该平台会收到大量有关车辆,车站和城市,各种停车场和各种补给品的数据和特征,汇总并为世界各地的这些车辆创建了大量潜在的时间表。该时间表考虑了从可为车辆加油,中间停车的类别中得出的大量条件。

系统中有许多不同的方法可以在1-2种情况下使用,也许甚至没有一个客户会记住。然后,考虑所有功能及其组合,编写烟雾测试实际上会变成针对整个系统的编写测试。

冒烟测试应检查输出是否正常运行,并且不会造成最小破坏。系统启动并以某种方式正常工作的非常原始的烟雾测试在我们的案例中没有带来任何好处。假设我们检查到数据库是否存在连接,是否正在启动,UI正在获得某种API。然后向左走,向右走-没有任何效果。就是说,有一个冒烟测试,但是生产中仍然存在错误。

在这个系统中,单元测试工作得很好:当清楚地监视到功能未更改时,在某些代码更改后它们不会损坏。代码也不同。不同的项目,不同的任务需要不同的测试方法。

我目前正在研究的想法只能有条件地称为自动生成测试。它是一个开发人员工具。我想要一个可以为我编写测试并运行所有无需我就能运行的代码的工具。

我举一个例子。有一个小的函数需要一个字典,并从中获得一些值和一个键。这个密钥对业务非常重要,但是从代码的角度来看,它是一个相当原始的操作:即使是嵌入密钥的几次,也要从字典中获取;检查他在那里,他不为零;交换它,或者只是返回值。从AST的角度来看,这是相当原始的代码。我不想浪费我的时间去写测试。我要汽车为我做。

这正是具有输入代码和输出代码的元程序。比方说,对py模块说:“在这里,我有一个断言,我”协助“您,在这种情况下会出现加薪错误,在这种情况下会返回有效值,而这种论点还会发生其他情况” 。就是说,实际上,它完成了我本人将研究馈送到函数输入的内容并将其写入测试的工作。

我希望程序生成它自己可以为我运行的最小值。但这应该是一个测试文件,如果需要,您可以在其中进行更改或扩展。您可以在Git中进行提交,测试测试等。

-您可以在多大程度上依靠这种自动生成的测试?我的意思是-它们与特定的实现有多少关系,在业务逻辑或重构的正常变化下它们将如何表现?

-想法是采用当前形式的代码,并基于此代码生成当前的有效测试。

当然,您可以每次都重新生成测试,但这不是正确的,因为这样就不会跟踪代码更改的状态。因此,为此仍然存在测试差异,即,仅针对之前未涵盖的内容生成测试。并且已经创建的测试需要您自己的支持。

也许这有点偏执,但是到目前为止,我怀疑自动生成是否可以保证通过重新生成测试,您将不会覆盖有效代码的有效代码。当我在2019年2月生成测试时是一回事,如果您更改逻辑,则您自己更改测试,因为您知道已进行了哪些更改。您知道测试失败的原因,并且可以相应地更正测试。每次重新生成它们时,情况就完全不同了。测试将是有效的,但仅对代码的更改状态有效。
我想为开发人员提供工具,而不是为增加代码覆盖率的工具。

-什么是成功指标?如何理解我们很好地生成了测试?

我要说出我要注意的内容,否则我觉得测试没有意义。必须在测试中处理开发人员描述的所有代码行为情况。例如,如果有一个if则不返回任何内容,但是写了一个日志,则在测试中该日志应该可以工作。人们不仅写警告和打印。因此,如果某处存在引发错误处理,则需要在测试中进行处理。如果突然消失,也就是说,代码逻辑将发生变化,那么这也需要解决。

同样,如果存在if语句,则每个条件的断言中都必须进行处理。那么测试将或多或少接近真相。并且不要忘记,这应该全部开始,而不仅仅是在PyTest中使用空的测试主体发出“成功”。

-告诉我在技术上有多困难。听起来是一项艰巨的任务。

是的,这是一项非常艰巨的任务,很可能是这个事实以及其他一些情况使我在有关莫斯科Python Conf ++ 报告中谈到了这一点。我想提出这个话题,让其他人感兴趣,并与他们讨论解决方案。

我觉得没有人会尝试这样做,因为这项任务很困难。否则,网络上会出现一些工件,例如代码,描述,文章,或者至少提到存在这种东西,但是它被废弃了。

要了解这有多困难,让我们回顾一下口译员的工作方式。有操作,代码中的语句,解释器执行它们-好,不好,失败,没有失败-并产生结果。此外,开发人员手动添加新参数,再次启动解释器,确保现在一切都成功。但是,当您尝试为代码生成测试时,首先需要遍历AST树并了解需要采取哪些步骤才能获得结果。

一个函数可以具有许多组参数,参数策略以及这些策略的许多结果。说到战略,我的意思是说有if arg_1==1: raise error。这意味着某些组的arg_1=1函数总是会返回错误。但是使用参数,arg_1>2函数结果将有所不同,并且将创建第二个组,第二个策略。

因此,我们需要找到并突出显示所有此类参数组(当然,如果存在),函数将在其中更改其行为。然后遵循一系列操作:这些参数将在函数内部发生什么以获得最终结果。

此外,我们不会忘记,除了存在某些参数的事实外,函数内部还存在其他操作,例如,分配变量,调用其他函数。也就是说,我们还获得了方法在方法上的依赖关系图,当要检查某些代码时,必须首先获取另一个代码的结果。

因此,要生成测试,您必须首先从AST树中获取所有必要的信息,然后为每种策略生成参数,参数和数据。有了它们,就可以遍历整个动作链,得到结果,然后我们才能进行具有不同断言的有效测试。这是一项艰巨的任务。

我不认为有一天有可能100%自动涵盖各种情况,例如,庞大的Django源代码画布。这很费力,但很有趣。到目前为止,我只是好奇我在哪里有耐心和力量。

-是否有其他语言和类似领域的例子?

-没有已知的类似的。我认为是因为编写测试比切割特殊工具要容易。
但是我有一种感觉,我们迟早会自动化我们已经做好的事情。
有大量开发人员可以很好地编写单元测试。我们在Python开发方面具有足够的能力,想要编写一个可以为我们完成此任务的工具或库。我们将编写更复杂的东西,更复杂的测试。

Java,C和.Net中都有某种类型的测试生成。但是,那里的一切也都更加基于财产或基于合同。在C语言中,有一个逐个字符的测试生成,似乎只看代码并在此基础上进行了一些测试。但这是语言本身的抽象层次如此不同,因此我不确定这是否是一个相似的故事。

如果有非常相似的东西,那么,当然可以采用一些东西,偷看。

-您是否认为框架或编写Python代码的技术简化或使从AST树生成测试的任务变得复杂或复杂化?

-从这个意义上说,简单地导入某些库或使用直接特定的框架是否有很大不同,很难说。绝对地,它可以极大地改变某些事物的工作,这些事物会改变代码过程的解释行为,例如C扩展。我还不知道如何处理这个问题,但是到目前为止,我最喜欢的第三个软件包在此问题上的使用取决于解决导入问题的需要。使用内置软件包,一切都很简单,但是使用导入,一切变得更加复杂。 Mypy有一些想法和实现,但是我还没有涉及导入第三方软件包的历史。

-也许是某种技术-很多动态特性,getattr的使用-诸如此类?还是工作正常?

“它工作得很好。”因为getattr或带有元类的操作在AST中可见。是的,它们需要解决,这增加了一些复杂性。但这还是可以跟踪的。

-我们已经说过,自动生成的测试主要是针对人的。它们对人们的可读性如何?断言,每个测试中会有很多逻辑吗?代码和数据之间的分隔是什么样子,您怎么看?

-现在,我尝试首先将各种平庸的事物添加到测试中。假设,如果是某种加薪错误,则不仅是加薪,而且至少要发表评论,什么样的错误,为什么会弹出,以便该人在阅读测试后了解实际发生的情况,哪种论点导致哪种错误。

到目前为止,断言以一种方法组合在一起。也就是说,如果有一个函数并且要检查的状态有5种,那么直到5个断言进入函数内部为止。

有一种想法可以引入名称约定,例如:将错误放在错误的末尾,测试日志也有自己的特色。但是我现在将其推迟了,因为如何在代码中直接创建带有测试的文本块的代码来创建最终测试类型的问题是成本最低的操作。如果突然想到需要重新格式化所有内容的想法,那么这将很容易进行-有现成的组装断言,您只需要为测试选择不同的外观即可。

-您支持unittest还是pytest?

-Pytest。只是因为我现在不想在输出上花费很多精力。 Pytest很好,因为它有许多易于使用的插件,装饰器和各种修饰符。

对于最终用户和开发人员而言,美化都可能很重要。但这根本不影响该想法的发展。如果您需要支持单元测试,则可以轻松添加。

-这种方法与基于属性的测试有多少关系?

-现在,要生成参数,只使用moki类型:您需要int,并提供随机int。但是,这样的策略将很容易重写,例如,开始使用假设。尽管我并没有花很多时间和精力,但是我知道我可以使用第三方生成器来获得价值。在我看来,这并不像使用AST那样重要。

-您打算支持合同编程还是以某种特殊方式分离?因为从原则上讲,它在单元测试,基于属性的测试和测试方面大有帮助,有助于理解业务逻辑。

-如果通过合同编程来表示代码中的合同,那么我将尽可能地偏离合同。因为当您可以使用合同编程时,您基本上可以使用合同对合同进行编码,并根据合同生成单元测试。然后,我的工具就不需要了。

现在,我尽量不要考虑任何会修改代码的事情。因为,例如,在外包项目中,我面临着缺乏测试的问题-不幸的是,在目前的公司中,这些几乎是所有项目-几乎不可能触及代码。也就是说,只有在您可以保证此装饰器或协定不会更改代码的整个功能组件之前,才能进行更改。
如果可以编辑代码,那么合同测试是好的。
但是现在,我从没有这种可能性的事实出发。因此,实际上,基于合同,您可以生成单元测试,并且实际上可以实现功能的重复。

-告诉我们下一个要点:如何测试收到的测试,以及您可以保证这些测试真正测试某些内容吗?

- 变异测试尚未取消,在理想的情况下,肯定需要在故事中使用它。总体而言,该想法与测试是由开发人员手动编写的相同。也就是说,可用于测试的所有内容都可以完全应用。

-现在让我们讨论一下莫斯科Python Conf ++会议。我们将执行我们多次提到的假设开发者之一。您想问他什么?

-我想问Zach关于他们想与维护者一起在哪里开发该项目的内容:添加什么,开发哪种方式。我肯定知道Zach现在具有用于生成测试的PR。他们定期做。更准确地说,装饰器会添加到现有的单元测试中。

我想从假设如何看待,贡献者如何看待方面讨论自动测试生成的思想。当然,从事这种级别的测试的人会有一些想法,或者也许有人已经尝试过一些东西。

“在准备会议日程时,我们指望这一点:让报告为讨论设置主题,在此期间每个人都将找到新的想法和发展方向。您会去看什么报告?

-我很沮丧,想去12点的所有报告。目前,将有Zac Hatfield-Dodds,Andrey Svetlov撰写有关异步编程报告,以及Vladimir Protasov进行重构自动化。我将进入最后两个中的一个,然后在报告结尾处运行 Zach(编者注:将一生的技巧付诸实践-几乎完全聆听新话题,然后来到报告末尾并向要与之交谈的发言人提问) 。

一定很有趣报告数据验证,我直接对此感兴趣。另外,我还将参考两个报告,但它们都将与我的报告同时进行:这是Vitaly Bragilevsky关于打字报告和Christian Heimes 关于剖析的报告。不幸的是,我无法以任何方式接近他们。

-告诉我一些有关报告主题的信息,您为什么做,你在做什么,为什么要讲话以及演讲中还等什么?

-我需要更多用于自动化开发过程的工具以及与此相关的更多协作。有这样的活动,但是在不断编写相同代码的背景下,在我看来应该有更多的活动。

如我所说,在Python中自动生成测试没有开放经验。目前尚不清楚是否有人在这样做,如果这样做,为什么没有起飞,没有前进。我不知道基于AST的测试对社区有多大意义,它能走多远。现在,我这样做是因为我对过程本身感兴趣,对挖掘AST树,对Python代码的工作方式有更多的了解,以及在使用顶级代码时遇到的许多细微差别,都感兴趣。使用AST树会带来大量突然发现。

我希望人们在报告后有想法,例如,如何使他们在工作中使用的东西自动化。这样一来,他们中的一些人便不再编写每天已经编写的代码,而开始生成或减少编写它们的时间。我希望有人对如何解决这个问题有了新的认识。

-您在哪里花时间在会议上发言,编写自己的图书馆?这个问题实际上不断出现,许多人抱怨说他们没有时间做任何事情。

-首先,关于时间。我对许多公司而言并不是一个非常方便的员工,因为我不会做对我而言似乎无效的事情。我尝试做一些对我来说真的很有趣的事情,或者我可以做有效而正确的事情。例如,如果经理要我立即修复某种错误(实际上不是错误,而是新客户的愿望清单),那么我就不会坐下来修复所有错误,因为我知道客户会回来并说出您为什么做到了。
我尽量不要在工作中做不必要的工作,不要做会造成我以后时间浪费的事情。
假设,如果他们要求我在星期五部署,我说:“伙计们,我非常爱你们,你们都是好人,但是如果您现在需要部署某些东西,请部署自己,我会回家。我可以在星期一进行部署,我们可以讨论为什么会发生这种情况,而您想在星期五进行部署。”第一次告诉客户或经理这可能会很痛苦,但是后来人们习惯了,学习了并且不要求您在星期五晚上做一些非常紧急的事情。他们了解,首先,没有人在上周五死亡,当时没有人被洪水淹没,甚至没有人赔钱。我尽量不要做会伤害我的事情。

关于错误的故事相同-如果必须不断修复许多错误,问题是:为什么会出现这些错误。我们不应该修复它们,而是考虑为什么它们如此之多,它们来自何处并主要与根本问题作斗争。当经理或客户表示迫切需要修复生产中的功能时,这些问题始终都是痛苦的问题。但是您必须能够说,如果我现在触摸此代码,那么也许您具有该功能以外的其他功能,那么您将无法进行生产,因为该代码未包含在测试中,因此您无法在其中添加其他代码,因为我们不记得其他六个人做什么。

有时,您需要克服困难并开始说话。这并非总是可能的,有必要提高一定的意识,即您花多少时间在哪种工作上是负责任的。

因此,我可能有时间。因为我试图优化我的工作时间,所以要花一定时间才能完成一项任务。同时,我知道在一个好的结构中,应该有1-2个小时的技术债务和一些改进。

我不会说我不起床工作8个小时。我将看一个坐下来写代码8个小时的开发人员。如果您平时工作,那么2个小时就是各种测试,代码审查,技术债务,代码上的“嗡嗡声”。 3小时是解决当前问题的方法,一个小时与经理沟通。剩下的2个小时是出于某种原因,用于与团队和自由职业者进行讨论。

您有兴趣做某些事情-您会做,而当您没有力量时,它们就会给您力量。当我做我目前感兴趣的事情,而不是我需要做的事情时,我有很多不同的活动-这可能称为有用的拖延。如果您学会在有趣的事物和仍然需要的事物之间进行选择,那将是最成功的。您只是不浪费时间浪费自己去做自己不想要的事情。

没有秘密,您只需要做自己喜欢的事,但同时又不会损害周围的人和项目。

有关从Python代码实现测试生成以及解决Python开发人员的许多其他任务的详细信息,请访问我们推迟到9月15日的Moscow Python Conf ++

All Articles