
嗨,我是Vimbox平台的全栈开发人员Alexey。 当我来到Skyeng时,他们在这里决定是否值得花时间在自动测试系统上,并请我分享以前的工作经验。 我有这样的经验:当我们离开之前的位置时,我们用php编写并扭曲了3000多个测试。 结果,我做了一个小的内部演讲,讲述了在开发这些自动测试的几年中我设法克服的难题,他们在速度,代码可读性和整体效率方面都处于挣扎状态。 该演示文稿对同事似乎很有用,因此我将其放在正文中也对更广泛的受众有用。
首先,将在本文中讨论的术语:
- 验收测试 -端到端测试:浏览器或浏览器模拟器在此处执行脚本
- 单元测试 (单元测试)-方法测试
- 功能测试 -涉及前端的控制器或组件的测试
- 装置 -测试工作所需的测试环境的状态(全局变量,数据库中的数据以及测试脚本中的其他参与者)
不同类型测试的优缺点

验收测试
- 优点:从名称上显而易见,此类测试从上到下覆盖了整个系统,请确保一切正常。
- 缺点:这些测试的反馈很慢,它们工作了很长时间,不是很可靠,存在很多误报。 在上一份工作中,我们还面临这样一个事实,即网络驱动程序无法检测到我们亲眼所见的某些元素。 现在可能已经解决了,但是后来我不得不放弃了。
单元测试
- 优点:易于编写,工作迅速。 它们只覆盖一小段代码,您不需要很多状态,因此,您也不需要大的固定装置。
- 缺点:对代码的体系结构或内部结构的更改不稳定。 如果需要将两个方法合并为一个或单独,请选择一个类,然后删除一个方法,则必须重写测试。
功能测试是一个中间解决方案。
- 优点:接受度更高,比模块化更能抵抗代码结构的更改。
- 缺点:比模块化慢,更难编写,因为 需要准备一个大的夹具。
为速度而战
在以前的工作中,我们编写了许多功能测试,而主要的挑战是响应速度。 即使在开发人员的计算机上本地启动,我也不得不等待很长时间才能获得结果。 速度是如此之慢,以至于无法应用“通过测试开发”的方法,因为它涉及每小时运行几次自动测试。 发现了瓶颈-使用数据库。 怎么处理呢?
体验第一:moki
PhpUnit中的Mock是一个动态创建的对象,其类是从模仿的类中动态继承的。 您可以配置mok的方法将返回的内容,可以检查moq的哪些方法调用多少次参数
moki的主要优点-它们使您可以切断全部功能。 用moch代替该服务,我们无需考虑那里发生的事情,开发其他脚本和固定装置,以便一切正常运行。 结果是:由于我们切断了执行对数据库查询的额外代码,因此减少了固定装置,并提高了响应速度。
暴民的内在优势在于,它们使人们更好地组织成瘾。 当您编写代码时,知道有必要在上面写一个测试(用mokami代替某些东西)时,您会立即想到依赖项。
减号 :测试代码过于附加于实现。 在测试过程中,我们必须创建一个模拟对象并考虑应在该对象上调用什么方法。
发现的第二个缺点是测试变得不太可靠。 他们“甚至没有注意到”界面的更改,更不用说实现了。 即 我们在某个地方删除了该方法,并在很长一段时间后发现覆盖该方法的测试似乎仍然没有任何反应,因为我们看到了它的模拟,他假装一切都很好。
我认为Mokas的经验无法加快测试速度。
体验二:SQLite
下一个选项是SQLite DBMS ,它可以在RAM中创建数据库。 我必须在SQLite中编写一个PostgreSQL转换器架构,每次迁移后都会生成一个新的SQLite架构。 通过该电路进行的测试在RAM中创建了一个空数据库。 这种方法将本地计算机上的测试速度提高了两到四倍。 每小时可以运行多次测试套件变得现实。
但是有缺点。 我们已经失去了许多本地PostgreSQL功能(json,一些便捷的聚合函数等)。 必须编写查询,以便它们可以在PostgreSQL和SQLite上使用。
体验三:PostgreSQL优化
这个决定是有效的,但带来了一些痛苦。 在某种程度上,我们了解到 PostgreSQL可以针对自动测试进行优化,从而将响应时间减少了大约四倍。 为此,向postgresql.conf添加一些设置:
fsync=off synchronous_commit=off full_page_writes=off
这些是可靠性设置,它们可以确保如果服务器在事务处理过程中死亡,则当一切重新开始工作时它将正确完成。 显然,此类设置无法在生产中进行,但在自动测试中非常方便。
此设置适用于整个集群,影响所有数据库,不能应用于任何一个数据库。 如果您设法在单独的群集中本地化数据库并在其中禁用fsync,这将非常方便。
关于new
我还要提及new
操作员的危险。 在其帮助下创建的服务不能用mokas和stub代替。 结论:
- 不要使用
new
创建本质上是服务的对象。 - 它可以在工厂中使用,因为它们可以更换。 但是工厂本身不应该通过
new
来创建。 - 它可以用于创建模型,实体,DTO(数据传输对象),值对象。
三年经验总结
- 在以前的工作中,我们拒绝了验收测试,但是现在我将再次尝试它们:Web驱动程序中很可能修复了许多错误。
- 如果需要通过测试涵盖新功能 ,则只需编写控制器/组件的功能测试。 在这种情况下,我们极有可能发生结构更改,单元测试对其不稳定。
- 这样的测试应该不多,因为许多==缓慢,因此它们的工作速度不如模块化测试快。 值得仅涵盖那些可以“解决”的情况(它们将来可能会出错)。
- 单元测试是用算法丰富的方法(需要测试的复杂逻辑)或将来结构更改风险很小的方法编写的。
- 摩卡的缺点通常超过了优点。 仅将它们用作替换外部API的网关是有道理的,有时还可以使用来自遗留代码的服务,这很难测试。
- 如果您决定在不进行测试的情况下编写代码,则建议您考虑一下:“在创建代码时,如果将来我们仍要为其编写测试呢?”
- 测试应该易于编写,并且具有可靠性,信心,有助于更好地理解代码,管理依赖关系。
- 注意测试的可读性。 必须以与测试代码相同的方式关联测试代码。
- 数据库装置-测试的一部分,也应该可读