大家好! 我叫Pasha,我是Lamoda订单处理团队的质量检查工程师。 我最近在PHP Badoo Meetup上发言。 今天,我想提供一份报告的笔录。
我们将讨论Codeception,如何在Lamoda中使用它以及如何在其上编写测试。
拉莫达提供许多服务。 有些客户服务可以直接与我们的用户,网站的用户以及移动应用程序进行交互。 我们不会谈论它们。 我们公司称之为深层后端-这些是我们的后台系统,可以使我们的业务流程自动化。 其中包括照相馆和呼叫中心的交付,存储,自动化。 这些服务大多数都是用PHP开发的。
简单讲一下我们的堆栈,这是PHP + Symfony。 Zend'e上有很多旧项目。 PostgreSQL和MySQL用作数据库,Rabbit或Kafka用作消息传递系统。
为什么要使用PHP后端?因为它们通常有一个分支API-它是REST,所以在某些地方有一些SOAP。 如果他们有一个用户界面,那么这个用户界面更像是一个辅助用户界面,供我们的内部用户使用。
为什么我们需要在Lamoda进行自动测试?总的来说,当我来到Lamoda工作时,有一个口号:“让我们摆脱手动回归”。 我们不会手动测试任何回归。 我们致力于这项任务。 实际上,这是我们需要进行自动测试的主要原因之一-以免手动推动回归。 我们为什么需要这个? 有权迅速释放。 这样我们就可以不费吹灰之力,迅速地发布我们的版本,同时从自测中获得某种网格,它们会告诉我们好坏。 这些可能是最重要的目标。 但是有两个辅助功能,我也想谈一谈。
为什么我们需要自动测试?
- 不要用手测试回归
- 快速释放
- 用作文件
- 加快新员工入职
- 自动测试可以方便地(在某些情况下)用作文档。 有时,进行测试,查看涉及的案例,它们如何工作以及了解此功能或该功能的工作方式,以及加快新员工(包括开发人员和测试人员)进入新项目的过程更加容易。 当您坐下来编写自动测试时,将立即清楚系统是如何工作的。
好的,谈到我们为什么需要自动测试。 现在让我们讨论一下我们在Lamoda中编写的测试。

这是一个相当标准的测试金字塔,从单元测试到端到端测试,已经在其中测试了一些业务链。 我不会谈论下面两个级别;将它们涂成这样的白色并不是没有道理的。 这些是对代码本身的测试,由我们的开发人员编写。 在极端情况下,测试人员可以进入“拉取请求”,查看代码并说:“好吧,这里的情况还不够,让我们来谈谈其他事情。” 这样就完成了测试人员用于这些测试的工作。
我们将讨论由开发人员和测试人员编写的上述级别。 让我们从系统测试开始。 这些测试可测试API(REST或SOAP),测试某些内部系统逻辑,各种命令,在Rabbit中解析队列或与外部系统交换。 通常,这些测试是非常原子的。 他们不检查任何链,而是检查一个动作。 例如,一种API方法或一条命令。 他们会检查尽可能多的正面和负面案例。
继续,端到端测试。 我将它们分为两部分。 我们的测试可以测试大量的UI和后端。 有些测试我们称为流程测试。 他们测试了链条-对象从头到尾的寿命。
例如,我们有一个用于管理订单处理的系统。 在这样的系统内部可以进行测试-从创建到交付的订单,即将其传递给所有状态。 在此类测试中,观察系统的工作原理非常简单容易。 您会立即看到某些对象的整个流程,这些对象与所有外部系统相互作用,为此使用了什么命令。
由于内部用户使用了此UI,因此跨浏览器访问对我们而言并不重要。 我们不会在任何服务器场上进行这些测试,对于我们来说,签入一个浏览器就足够了,有时甚至不需要使用浏览器。
“我们为什么选择Codeception进行测试自动化?” -你可能会问。
老实说,我对这个问题没有答案。 当我来到Lamoda时,Codeception已经被选作编写自动测试的标准,而我实际上发现了它。 但是在使用该框架一段时间后,我仍然理解为什么使用Codeception。 这就是我想与您分享的。
为什么要进行代码接收?- 您可以编写和运行任何类型的相同测试(单元,功能,验收)。
- 已经解决了许多缺陷,已经编写了许多模块。
- 在所有项目中,尽管需求略有不同,但测试将看起来相同。
- Codeception的概念建议您在此框架上编写任何测试:单元,集成,功能,验收。 而且,至少您会同等地推出它们。
- Codeception是一个功能强大的处理器,其中许多问题,许多问题,许多测试任务已经解决。 如果不确定,则很可能会从外部找到一些东西-一些特定工作的附加组件。 您无需为数据库编写任何其他测试包装。 只需将模块连接到Codeception并使用它们即可。
- 好吧,这样的加分(当您有许多项目和服务时,它可能更适合大型公司)–在所有项目中,测试的外观将是正负相同。 这很酷。
我将简要介绍一下Codeception的样子,因为许多人都在使用它。

编解码在演员模型上工作。 将其拖动到项目中并进行初始化后,将生成这样的结构。
我们有yml文件,在下面
-functional.suite.yml ,
integration.suit.yml ,
unit.suite.yml 。 他们创建测试的配置。 每种测试都有爸爸,这些测试在哪里,有3个辅助爸爸:
_
数据 -用于测试数据;
_
输出 -放置报告的位置(xml,html);
_
支持 -在测试中使用一些辅助助手,函数以及您编写的所有内容。
首先,我将告诉您我们从Codeception中学到了什么,并且可以立即使用它,无需进行任何修改,也不能解决其他任务或问题。
标准模块- Php浏览器
- 休息
- b
- li
- AMQP
第一个这样的模块是PhpBrowser。 该模块是Guzzle的包装,使您可以与应用程序进行交互:打开页面,填写表单,提交表单。 而且,如果您不关心跨浏览器和浏览器测试,则如果您突然在测试UI,则可以使用PhpBrowser。 通常,我们在UI测试中使用它,因为我们不需要任何复杂的交互逻辑,我们只需要打开页面并在那里做一些小事情。
我们使用的第二个模块是REST。 我想这个名字很清楚他在做什么。 对于任何http交互,都可以使用此模块。 在我看来,几乎所有的交互都可以在其中解决:标头,Cookie,授权。 您需要的一切都在其中。
我们现成使用的第三个模块是Db模块。 在最新版本的Codeception中,不支持一个,但已添加了多个数据库。 因此,如果您的项目中突然有多个数据库,则现在即可使用。
Cli模块,它允许您运行测试中的
shell和
bash命令,我们也使用它。
有一个AMQP模块可与基于此协议的任何消息代理一起使用。 我想指出,它已经在RabbitMQ上进行了正式测试。 由于我们使用RabbitMQ,所以一切都好。
实际上,至少在我们的情况下,Codeception可以满足我们所需的所有任务的80-85%。 但是我仍然不得不做一些事情。
让我们从SOAP开始。

在我们的服务中,某些地方有SOAP端点。 他们需要测试,拉扯,与它们有关。 但是您会说在Codeception中有一个这样的模块,它允许您发送请求,然后对答案进行处理。 以某种方式解析,添加检查,一切正常。 但是,SOAP模块不能与多个SOAP端点一起使用。

例如,我们拥有具有多个WSDL,多个SOAP端点的整体。 这意味着在Codeception模块中无法在yml文件中对其进行配置,以便它可以与多个文件一起使用。

Codeception具有动态模块重新配置功能,您可以编写某种适配器来接收例如SOAP模块并对其进行动态重新配置。 在这种情况下,有必要替换端点和使用的方案。 然后在测试中,如果您需要更改要向其发送请求的端点,我们将获得适配器并将其更改为新的端点,新的电路并向其发送请求。

在Codeception中,没有与Kafka一起工作,也没有第三方或多或少的官方插件可以与Kafka一起工作。 不用担心,我们编写了模块。

因此,它是在yml文件中配置的。 为经纪人,消费者和主题设置了一些设置。 这些设置在您编写模块时,然后可以使用Initialize函数将其拉入模块,并使用相同的设置来初始化此模块。 而且,实际上,该模块还有其他所有要实现的方法-将消息放入主题中并阅读。 这就是您所需的全部内容。
结论 :用于Codeception的模块易于编写。
来吧 就像我说的,Codeception有一个Cli模块-Shell命令的包装器,并处理它们的输出。

但是有时
shell命令不需要在测试中运行,而需要在应用程序中运行。 通常,测试和应用程序是稍微不同的实体,它们可以位于不同的位置。 测试可以在一个地方运行,而应用程序可以在另一个地方。
那么,为什么我们需要在测试中运行
shell ?
我们在应用程序中具有命令,例如,在RabbitMQ中解析队列并按状态移动对象。 这些处于亲模式的命令是从主管下启动的。 主管监督其实施。 如果它们掉落,那么他会再次启动它们,依此类推。
当我们测试时,主管未运行。 否则,测试将变得不稳定,不可预测。 我们自己希望控制应用程序内部这些命令的启动。 因此,我们需要从应用程序中的测试运行这些命令。 我们使用两个选项。 一个,另一个,原则上,一切都一样,一切正常。
如何在应用程序中运行
Shell ?
首先:在应用程序所在的同一位置运行测试。 由于我们在Docker中拥有所有应用程序,因此测试可以在服务本身所在的同一容器中运行。
第二个选择:为测试创建一个单独的容器,一些
测试运行器 ,但使其与应用程序相同。 也就是说,从同一个Docker映像开始,一切都将类似地工作。

我们在测试中遇到的另一个问题是使用各种文件系统。 以下是您可以并且应该使用的示例。 前三个与我们相关。 这些是Webdav,SFTP和Amazon文件系统。
您需要使用什么?
- Webdav
- FTP / SFTP
- AWS S3
- 本地的
- Azure,Dropbox,Google驱动器
如果通过Codeception进行泛滥,几乎可以找到几乎所有流行的文件系统的一些模块。

我唯一找不到的是Webdav。 但是,这些文件系统的正负功能在外部工作方面是相同的,我们希望以相同的方式使用它们。
我们编写了名为Flysystem的模块。 它位于公共领域的
Github上 ,并支持2个文件系统-SFTP和Webdav-并允许您使用相同的API使用这两个文件系统。

获取文件列表,清理目录,编写文件,等等。 如果您还在此处添加Amazon文件系统,那么我们的需求一定会得到满足。
我认为,下一点对于自动测试(尤其是系统级别)正在使用数据库非常重要。 总的来说,我希望它是-如图所示-VZHUH并且一切都启动了,它可以工作,并且在测试中应该较少支持这些数据库。

我在这里看到的主要任务是什么:
- 如何推出所需结构的数据库-DB
- 如何用测试数据填充数据库-Db,夹具
- 如何进行选择和检查-DB
对于Codeception中的所有3个任务,有2个模块-Db,我已经谈到过,另一个模块是Fixtures。
在这2个模块和3个任务中,我们仅将DB用于第三个任务。
对于第一个任务,您可以使用数据库。 在这里,您可以配置将要从中部署数据库的SQL转储,以及带有固定装置的模块,我认为很清楚为什么需要这样做。
将以数组的形式存在固定装置,可以将其持久化到数据库中。
就像我说的,我们在前两个任务上解决的方式略有不同,现在我将告诉您我们是如何做到的。
数据库部署- 用PostgreSQL或MySQL引发容器
- 我们将所有迁移与学说迁移一起进行
首先是关于部署数据库。 在测试中这是如何发生的。 我们使用所需的数据库-PostgreSQL或MySQL来引发容器,然后使用
doctrine migration滚动所有必要的
迁移 。 一切就绪,所需结构的数据库已准备就绪,可以在测试中使用。
为什么我们不使用阻尼器-因为这样就不需要支撑了。 这是测试所附带的某种转储,如果数据库中发生某些更改,则需要不断对其进行更新。 有迁移-无需维护转储。
第二点是测试数据的创建。 我们不使用Codeception的Fixtures模块,而是使用
Symfony捆绑软件来安装灯具。

这里有一个
链接 ,以及如何在数据库中创建固定装置的示例。
然后,您的夹具将被创建为该域的某个对象,可以将其存储在数据库中,并且测试数据将准备就绪。
为什么选择DoctrineFixtureBundle?
- 易于创建相关对象链。
- 如果用于不同测试的夹具相似,则数据重复较少。
- 更改数据库结构时的编辑较少。
- 夹具类比数组更可视。
我们为什么要使用它? 是的,出于同样的原因-这些灯具比Codeception的灯具更易于维护。 创建相关对象链比较容易,因为它们都在symfony捆绑包中。 需要复制的数据较少,因为可以继承固定装置,它们是类。 如果数据库结构发生更改,则始终需要编辑这些数组,而不必总是编辑类。 领域对象形式的夹具总是比数组更可见。
我们讨论了数据库,让我们谈谈moki。
由于这些测试是对整个系统进行测试的足够高的级别,并且由于我们的系统高度互连,因此很显然存在一些交换和交互。 现在,我们将讨论系统之间交互的关键。
莫克规则- 哭泣所有外部http服务交互
- 不仅检查正面情况,还检查负面情况
交互是一些REST或SOAP http交互。 我们正在润湿的测试框架内的所有这些相互作用。 也就是说,在我们的测试中,对任何地方的外部系统都没有真正的吸引力。 这使测试稳定。 通常,由于外部服务可能起作用,可能不起作用,响应可能缓慢,响应速度可能很快,所以不知道其行为是什么。 因此,我们将全部内容都包括在内。
我们也有这样的规则。 我们不仅在积极互动中加油,而且还在努力检查一些负面情况。 例如,当第三方服务响应第500个错误或产生一些更有意义的错误时,我们将尝试全部检查。
我们使用Wiremock进行模拟,Codeception本身支持...,它具有Httpmock这样的官方附加组件,但我们更喜欢Wiremock。 如何运作?
在测试期间,Wiremock会作为一个单独的Docker容器出现,并且所有必须发送到外部系统的请求都将发送到Wiremock。

Wiremock,如果您看幻灯片,有一个方框,即Request Mapping,它有一组这样的映射,表示如果这样的请求到达,则需要给出这样的答案。 一切都很简单:一个请求来了-收到了一个模拟。
可以静态创建模拟,然后在已经带有Wiremock的容器中创建这些模拟,这些模拟将可用,可以在手动测试中使用。 您可以通过某种测试在代码中动态创建。
这是一个有关如何动态创建模拟的示例,您会看到,说明非常具有声明性,从代码中可以很清楚地看出我们正在创建哪种模拟:此类URL的GET方法的模拟,以及实际上返回的内容。

除了可以创建该模拟的事实之外,Wiremock还有机会检查哪个请求发送到了该模拟。 这在测试中也非常有用。
关于Codeception本身,可能包括所有内容,以及关于我们的测试如何运行的几句话,以及一些基础架构。
我们在用什么?

好吧,首先,我们在Docker中拥有所有服务,因此启动测试环境将引发正确的容器。
Make用于内部命令,Bamboo用作CI。
CI测试运行是什么样的?

首先,我们构建所需版本的应用程序,然后创建环境-这是应用程序,以及它需要的所有服务,例如Kafka,Rabbit,数据库,然后将迁移转移到数据库。
所有这些环境都是在Docker Compose的帮助下提出的。 正是在CI中,所有容器都在Kubernetes下旋转。 然后运行测试并运行。
这需要多长时间?
所有这些都取决于特定的服务,但是通常来说,在运行测试之前提升环境的时间是5到10分钟(测试从6到30分钟)。

当所有测试都集中在一个线程中时,我会立即警告该问题。
好吧,这样的问题。 测试应该多久运行一次? 当然,越多越好。 您越早发现问题,就可以更快地解决它。
2 . , , unit, unit-. - , .
, . .
- — , , Codeception, . , .