如何Docker化端到端验收测试


【编者的话】本文详细描述了如何利用Selenium Docker镜像和CodeceptJS以及Express服务器在Docker容器中进行端到端的验收测试用例的执行,为开发者进行自己的验收测试用例的执行配置提供了一个很好的例子。
1._dockerlogo_.png

本文目标是充当使用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,你的测试应该看起来几乎相同。如果你(稍后)感觉到一个引擎的限制,你可以轻松地将测试切换到其他引擎。

测试工具层

2_layers_test_tools.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 for Chrome或GeckoDriver for Firefox。每个都充当一个独立的服务器,用于接收来自使用WebDriver API的客户端的请求(通常是测试所在的位置)。为了与目标浏览器进行通信,必须安装正确的WebDriver实现。

Selenium Server

如果测试用例是在定义它们的同一台机器上运行,那么使用的客户端WebDriver API实现(如下所述)可以直接与浏览器WebDriver通信,并且不需要Selenium Server

然而,如果要在不同的计算机上运行测试,无论是在CI中,在跨多个计算机或虚拟机(VM)的Selenium Grid设置中,在远程测试平台(如BrowserStackSauceLabs)上,还是在Docker中,都要运行Selenium服务器。它充当代理,将来自客户端WebDriver API的请求转发到正确的浏览器WebDriver,并从浏览器传回响应。
3_container.png

正如我们稍后将看到的,'machine'也可以换成'container'

客户端WebDriver实现

各种工具实现了客户端WebDriver线路协议。这里的协议可以看作是通过上述层向浏览器发送请求的API。许多使用该协议的工具都是完整的框架,例如WebDriverIO,它们包含自己的测试运行程序。

其他实现包括:原始的Selenium WebDriver,Protractor,Appium等。

每个库的目标是实现相同的结果,但焦点和API实现略有不同。比如Protractorbrowser.get(url)WebDriverIObrowser.url(url)

一个API统治所有

如本节开头所述,这是CodeceptJS发挥作用的地方。它将其他客户端WebDriver协议(或其它协议)实现为“帮助程序”,并允许你在使用一种API语言时指定你喜欢的帮助程序。 CodeceptJS并不关心所选帮助程序使用的协议。

WebDriverIO,Puppeteer,Protractor,Nightmare和Appium都是现有的帮助程序。

在CodeceptJS中,无论选择哪个帮助程序,上一个命令都是I.amOnPage(url)。这意味着如果你想将后端切换到支持的其他帮助程序之一,则不必重新编写测试。如果你愿意,可以通过自定义类覆盖或添加到默认API方法。

创建测试项目

有这么多层,这听起来开始复杂了,但在CodeceptJS的初始化脚本和Docker镜像之间,我们可以快速找到一个有效的例子。

我们将产生什么

4_two_containers.png

现在将使用两个容器,但这可以扩展

我们将在CodeceptJS中编写一个简单的测试,指定一个WebDriverIO后端帮助程序,它将与Docker容器中的远程单机firefox浏览器进行通信。我们将使用Express“hello world”应用程序,但这可以被你希望的任何应用程序取代。
5.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目录中。

我们的应用

接下来我们需要一个应用来测试。 为此,我们将从app.js开始运行Express"hello world"应用程序。
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-compose up启动Docker容器,并使用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 Server,驱动程序和浏览器,我们使用公共Docker Hub提供的预定义镜像selenium/standalone-firefox。我们指定我们想要的版本,3.12.0-americ。如果我们没有指定它,默认情况下将使用最新的镜像(这不是一件坏事)。我们建议将其配置为共享主机的内存以防止浏览器崩溃,并公开端口4444(默认的Selenium端口)。我们还将此映射到本地计算机上的端口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上运行。

注意:只有在第一次运行docker-compose up时,或者对Dockerfile或其中执行的步骤进行了更改时,才需要--build标志。例如,如果我们在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 -ti app npm run test:e2e

我们现在可以看到我们的测试正在运行,并在执行时看到每个步骤!从这里我们可以扩展我们的测试,添加额外的测试(以_test.js结束的文件名),并使用相同的两个命令来运行它们。无需更多设置。

你现在拥有一个易于扩展的E2E测试设置,无论运行哪台计算机,都可以依赖该设置以相同的方式运行。它是用API命令编写的,开发人员和非开发人员都可以轻松理解。现在剩下的就是决定你的应用应该具备哪种行为,然后进行测试!

最后的话

SeleniumHQ还生成用于Chrome测试的Docker镜像,以及使用Selenium Grid一次运行多个Chrome和Firefox实例的镜像。

CodeceptJS还有在Docker中运行CodeceptJS的说明,因此不需要在应用程序中将其指定为依赖项。

关于Docker如何工作的更技术性但仍然是初级的描述可以在我写的标题为亚马逊弹性容器服务的初学者指南的帖子的第一部分中看到。

感谢你的阅读。

原文链接:How to Dockerize your End-to-End acceptance tests (翻译:池剑锋)

0 个评论

要回复文章请先登录注册