如何Dockerize您的端到端验收测试
本文作为使用Selenium Docker映像以及CodeceptJS和Express服务器的“操作方法”指南。
其中,我们将涵盖:
- 什么是E2E验收测试?
- 为什么要使用Docker?
- 松散耦合的测试工具
- 测试工具层
- 创建测试项目
E2E验收测试
验收测试是典型软件开发过程中的一个阶段。它涵盖了测试,以确定产品是否符合总体要求规格,以及是否“已接受”为准备交付。在将产品投入生产之前,这通常是测试的最后阶段。这可能包括基于用户的验收测试,基于业务的验收测试,甚至包括alpha / beta测试。
端到端(E2E)测试是验收测试的一种实现。这是验收测试的方法,但这些术语不是同义词。它允许从头到尾测试应用程序的流程,看它是否按设计执行。在Web应用程序的情况下,它将涉及确定用户场景并测试用户将按顺序采取的每个步骤。如果方案未成功完成,则测试失败。
存在各种工具来自动化该过程,模拟用户与应用程序的交互。
为何选择Docker?
创建和运行E2E测试通常被认为是一个片状和复杂的过程。它需要大量的设置,在不同的机器或CI(持续集成)环境中运行时仍然很容易失败。为本地测试和CI测试安装和维护不同的浏览器和WebDrivers需要时间。即使完成了,它仍然会因为简单的问题而失败,例如开发人员本地计算机或CI中的屏幕分辨率是否不同。
Docker的标准优势也适用:无需处理操作系统兼容性或安装了依赖项。要运行Selenium Server,您需要安装Java(或者至少要显式启动/停止)。要运行Express,您需要Node.js,而对于Chrome,您需要Chrome本身以及ChromeDriver。
使用Docker时,可以消除这些依赖性。您只需使用已包含这些容器的不同容器,无论它们在哪台机器上运行,它们的功能都完全相同。当您认为将Docker构建到CI中是多么容易时,Docker化您的测试过程将成为一个明显的选择。
松散耦合的测试工具
在编写E2E测试时有几个框架可用,而对于新手来说,很难知道选择哪个并投入时间。如果选错了,你就会浪费很多时间。
这就是CodeceptJS的用武之地。CodeceptJS是一个测试框架,采用场景驱动的行为驱动开发(BDD)方法,使用API语言,非工程师易于理解和使用。然而,或许更重要的是,它是后端不可知的。它是在几个流行的测试库(例如WebDriverIO或Puppeteer)之上编写的,它的单个高级API可以与您选择的任何一个进行通信。它的创作者认为这
您的测试不应该绑定到您的执行引擎。无论您选择Selenium还是Puppeteer,您的测试应该看起来几乎相同。如果您(稍后)感觉到一个引擎的限制,您可以轻松地将测试切换到其他引擎。
测试工具层
![](https://cdn-images-1.medium.com/max/1600/1*p0ALhZOLfRFYBvosshCmjQ.png)
模拟用户与浏览器交互的每一层都有一些不同的产品
让我们采用自下而上的方法来研究每个层如何构建在最后一层上。
最初引用Selenium是值得的,Selenium是一个长期运行的项目,其中包含一组活跃使用的工具,它们现在由Selenium WebDriver,Grid,Server和IDE组成。在创建这些工具时,他们设定了许多行业标准,通常以他们来自Selenium产品的名字而令人困惑地命名。这可以在'Selenium WebDriver'和在其开发中建立的'WebDriver线协议'中看到。适用的那些将在下面更详细地显示。
浏览器
任何网络浏览器:Chrome,Firefox,Internet Explorer,Safari,Opera等。通常在文档中称为“用户代理”。
W3C的WebDriver线协议
WebDriver有线协议是一种与Web浏览器交互的平台和语言中立方式。它定义了一个在请求/响应对中使用JSON over HTTP 的通用RESTful Web服务。它允许从外部源操作DOM元素,同时还允许导航到网页,用户输入,JavaScript执行等。
最初由Selenium为Selenium WebDriver编写,该协议现已进入编辑草案阶段,成为官方的W3C标准。
其他协议确实退出,但这里不会详细介绍。所有解释都将假设一个实现WebDriver有线协议的后端。
另一个值得注意的协议是Puppeteer使用的Chrome DevTools协议。Puppeteer不使用Selenium Server,并捆绑了最新版本的Chromium,供本地使用。如果你想在Docker中运行Puppeteer,你可以使用CodeceptJS图像(Puppeteer和Nightmare附带)或者按照官方指南创建可以支持它的自定义图像。
浏览器WebDrivers
这些是金属丝的webdriver协议的浏览器的特定实现中,例如ChromeDriver用于Chrome或GeckoDriver用于Firefox。每个都充当一个独立的服务器,用于接收来自使用WebDriver API 的客户端的请求(通常是测试所在的位置)。为了与目标浏览器通信,必须安装正确的WebDriver实现。
Selenium Server
如果测试是在定义它们的同一台机器上运行,那么使用的客户端WebDriver API实现(如下所述)可以直接与浏览器WebDriver通信,并且不需要Selenium Server。
但是,如果要在不同的计算机上运行测试,无论是在CI中,在跨多个计算机或虚拟机(VM)的Selenium Grid设置中,在远程测试平台(如BrowserStack或SauceLabs)上,还是在Docker中,都要运行 Selenium使用服务器。它充当代理,将来自客户端WebDriver API的请求转发到正确的浏览器WebDriver,并从浏览器传回响应。
![](https://cdn-images-1.medium.com/max/1600/1*hmr8_c0WIcilEsMdfc-u-w.png)
正如我们稍后将看到的,'machine'也可以换成'container'
客户端WebDriver实现
各种工具实现了客户端WebDriver有线协议。这里的协议可以看作是通过上述层向浏览器发送请求的API。使用该协议的许多工具都是完整的框架,例如WebDriverIO,它们包含自己的测试运行器。
其他实现包括:原始的 Selenium WebDriver,Protractor,Appium等。
每个库的目标都是为了获得相同的结果,但焦点和API实现略有不同。这可以像Protractor browser.get(url)
和WebDriverIO 一样基本browser.url(url)
。
一个API来统治它们
如本节开头所述,这是CodeceptJS发挥作用的地方。它将其他客户端WebDriver协议(或其他)实现称为“ 帮助程序 ”,并允许您在使用一种API语言时指定您喜欢的帮助程序。CodeceptJS并不关心所选帮助程序正在使用哪种协议。
WebDriverIO,Puppeteer,Protractor,Nightmare和Appium都是现有的助手。
在CodeceptJS中,I.amOnPage(url)
无论选择哪个帮助程序,上一个命令都是如此。这意味着如果您想将后端切换到支持的其他助手之一,则不必重新编写测试。它可以覆盖或添加到通过自定义类,你应该想的默认API方法。
创建测试项目
有这么多层,这开始听起来很复杂,但在CodeceptJS的初始化脚本和Docker镜像之间,我们可以快速找到一个有效的例子。
我们将生产什么
![](https://cdn-images-1.medium.com/max/1600/1*wKrNJtA4OpbtHjhTRCsc5w.png)
现在将使用两个容器,但这可以扩展
我们将在CodeceptJS中编写一个简单的测试,指定一个WebDriverIO后端帮助程序,它将与standalone-firefox Docker容器中的远程浏览器进行通信。我们将使用Express“hello world”应用程序,但这可以被您希望的任何应用程序取代。
![](https://cdn-images-1.medium.com/max/1600/1*XT4px3Nj57WeVjdv6Rt7mg.png)
很快,只需要两个命令来运行Dockerized应用程序和所有测试套件
一旦我们完成了所有设置,我们将只能运行两个命令来运行测试:
docker-compose up --build
docker exec -it app npm run test:e2e
通过在两个并排的终端窗口中运行,我们可以看到容器正在运行并且测试实时执行。
先决条件
- Docker,适用于您正在开发的任何一台机器。
- 您还可以安装Node.js,&npm进行本地开发和调试,但这些也可以在Docker中完全运行。
文件结构
我们将在下面生成文件结构。你可以在Github上看到一个工作示例。
|-- .gitignore
|-- output/
|-- Dockerfile
|-- app.js
|-- docker-compose.yml
|-- package.json
|-- package-lock.json
|-- e2eTests/
|-- common_test.js
|-- docker.conf.js
依赖
首先,我们将使用Express作为依赖项创建package.json,将CodeceptJS和WebDriverIO创建为dev依赖项。
{ "name": "example-standalone-firefox", "version": "1.0.0", "description": "Example of Dockerizing E2E testing", "scripts": { "start": "node app.js", "test:e2e": "codeceptjs run --steps --verbose --config=./e2eTests/docker.conf.js" }, "dependencies": { "express": "^4.16.3" }, "devDependencies": { "codeceptjs": "^1.2.0", "webdriverio": "^4.12.0" } }
我们还包括两个脚本,一个用于运行我们将添加的Express应用程序(npm run start
),另一个用于运行我们的CodeceptJS测试(npm run test:e2e
)。
codeceptjs run --steps --verbose --config=./e2eTests/docker.conf.js
--steps
非常适合在测试运行时在终端中显示输出,同时--verbose
进一步扩展详细程度。--verbose
可能不需要作为标准,但有利于查看示例的工作原理。--config
向我们展示了后端配置文件的路径,在这种情况下保存在一个单独的e2eTests
目录中。
我们的应用
接下来我们需要一个应用来测试。为此,我们将运行Express“ hello world ”应用程序app.js
。
const express = require('express'); const app = express(); app.get('/', (req, res) => res.send('Hello World!')); const server = app.listen(3000, () => { const port = server.address().port console.log(`Example app listening on port ${port}`) })
您可以使用npm run start
然后localhost:3000
在浏览器中查看此内容。
测试配置
CodeceptJS需要两个文件,一个配置文件和一个测试文件。测试文件非常简单:它测试可以访问应用程序,保存屏幕截图,并检查页面上是否可以看到文本“Hello”。
Feature('Basic test'); Scenario('navigate to homepage', I => { I.amOnPage('http://app:3000'); I.saveScreenshot('frontpageScreenshot.png'); I.see('Hello'); });
我们将使用多个Docker容器的第一个迹象显示在使用app:3000
而不是localhost:3000
。localhost
只能在一个容器内理解。如果正在从另一个容器运行命令(在本例中是我们的第二个Selenium容器中的Firefox),那么它需要更明确的引用。我们可以直接使用第一个容器的IP地址,但使用容器的名称更容易阅读。
在这种情况下,app
将是运行应用程序的容器的名称,因此我们可以使用app:3000
。不要担心,如果你还没有遵循这一点,看看我们docker-compose.yml
的结构将有所帮助。
我们还需要一个主配置文件。这可以用JSON或JS编写,但在这里我们使用JS。我们来看看这个:
exports.config = { tests: './*_test.js', // how to know which files are test files output: './output', // where to save screenshots helpers: { WebDriverIO: { // which backend helper to use url: 'http://app:3000', // a base URL to start on host: 'firefox-container', // identifying where selenium runs browser: 'firefox', // a series of config options smartWait: 5000, waitForTimeout: 10000, desiredCapabilities: { // for a demo app we do not want acceptInsecureCerts: true, to worry about SSL certs } }, }, name: 'codeceptjs-docker', };
设置Docker
回到上面“我们将要生成什么”部分中的图表,我们可以看到我们将使用两个Docker容器。他们必须彼此了解并能够沟通。一个将包含我们的应用程序和测试,一个包含Selenium Server,GeckoDriver和Firefox,因此我们不需要在本地计算机上安装Firefox。
Docker Compose是一个“定义和运行多容器Docker应用程序的工具。”它使用命令 启动Docker容器docker-compose up
,并使用它来阻止它们docker-compose down
。如果正在使用用户定义的Dockerfile,--build
则用于构建它,或者第一次docker-compose up
运行,或者如果对Dockerfile进行了更改。docker-compose.yml
是定义up
命令将执行的操作的文件。
我们的下一步是创建这个docker-compose.yml
。它严重依赖于缩进。
version: "2" // which version of compose syntax you are using services: app: container_name: app // explicit so we can use this for app:3000 build: . // a self defined Dockerfile, see below ports: // exposes port 3000 (where express runs) - "3000:3000" to other containers, and to our local depends_on: browser - firefox-container volumes: // maps so changes to these can be seen - ./e2eTests:/e2eTests - ./package.json:/package.json - ./package-lock.json:/package-lock.json - ./.gitignore:/.gitignore - ./app.js:/app.js firefox-container: // we'll look at this below container_name: firefox-container image: selenium/standalone-firefox:3.12.0-americium volumes: - /dev/shm:/dev/shm ports: - "4444:4444"
对于我们的Selenium服务器,驱动程序和浏览器,我们使用可从公众预先定义的图像泊坞枢纽称为硒/独立Firefox的。我们指定我们想要的版本,3.12.0-americ。如果我们没有指定它,默认情况下将使用最新的图像(这不是一件坏事)。至于建议,我们将其配置为共享主机的内存,以防止浏览器崩溃中运行,并揭露4444端口,默认端口硒。我们还将此映射到本地计算机上的端口4444,允许我们localhost:4444/wd/hub/static/resource/hub.html
在浏览器中访问。
对于我们的app
容器,我们不只是使用由其他人构建的图像,而是编写Dockerfile来指定我们的应用程序的构建方式。与selenium-firefox
容器相同,我们在这种情况下公开了一个端口3000,因为Express是默认运行的地方。通过使用3000:3000
我们可以localhost:3000
在Docker中运行应用程序时访问它,以便在我们的本地浏览器中查看它。
我们的Dockerfile使用公共node:carbon
映像作为基础,设置工作目录,将一些文件从本地计算机复制到容器,运行npm install
以便容器具有所有需要的依赖项,然后运行npm start
我们指定的命令。
FROM node:carbon WORKDIR ./ COPY ./package.json ./package-lock.json ./app.js ./ RUN npm install CMD [ "npm", "start" ]
这意味着在docker-compose up --build
运行时,它将遵循这些步骤,从而使我们的应用程序准备好并在端口3000上运行。
注意:--build
只有在第一次docker-compose up
运行时,或者对Dockerfile或其中执行的步骤进行了更改时才需要该标志。例如,如果我们在我们中添加了另一个依赖项,package.json
那么如果我们没有重建我们的映像,那么Docker就不会知道它,就像npm install
在Dockerfile中运行一样。
运行测试
我们现在有一个简单的应用程序,为它编写的测试,以及将运行我们的应用程序,Selenium Server和Firefox的Docker Compose配置。
我们可以开始使用所有这些docker-compose up --build
。
要在正在运行的Docker容器中运行命令,docker exec
可以从另一个终端窗口使用。格式为:
docker exex <flags> <container_name> <command>
我们将使用的命令是:
docker exec -it app npm run test:e2e
我们现在可以看到我们的测试正在运行,并在执行时看到每个步骤!从这里我们可以扩展我们的测试,添加其他测试(结束文件名_test.js
),并使用相同的两个命令来运行它们。无需更多设置。
您现在拥有一个易于扩展的E2E测试设置,无论运行哪台计算机,都可以依赖该设置以相同的方式运行。它是用API命令编写的,开发人员和非开发人员都可以轻松理解。现在剩下的就是决定你的应用应该具备哪种行为,然后进行测试!
最后的话
SeleniumHQ还生成用于Chrome测试的 Docker 镜像,以及使用Selenium Grid一次运行多个Chrome和Firefox实例的图像。
CodeceptJS还有在Docker中运行CodeceptJS的说明,因此不需要在应用程序中将其指定为依赖项。
在我写的一篇名为“ 亚马逊弹性容器服务的初学者指南 ”的文章的第一部分中,可以看到关于Docker如何工作的更具技术性,但仍然是初级的描述。
感谢您阅读😀
更新:
我最近为寻找测试复杂应用程序的后续步骤的任何人编写了Customizing CodeceptJS e2e测试。
资源
- CodeceptJS Docker示例的Github
- CodeceptJS快速入门指南
- Selenium WebDriver架构
- Selenium WebDriver Flow