Testing Ansible Web App Roles with Selenium

Michael Lynch

使用 Selenium 测试 Ansible Web 应用角色

概述

Ansible 是一款部署 Web 应用的优秀工具。Ansible 让我们可以通过组成 Web 应用的不同“角色”来定义 Web 应用(例如 Web 服务器、数据库服务器、应用服务器)。随着角色及其之间的交互变得越来越复杂,我们需要相应更强大的方式来测试这些角色,以验证我们是否正确部署了 Web 应用。

上一篇博文中,我们使用 Ansible 部署了一个名为 ClipBucket 的视频托管 Web 应用。在那篇博文中,我们加入了自动化测试来验证 Web 应用是否已正确安装,但这些测试并没有非常严格地检验应用功能。

在这篇博文中,我们将展示更强大的自动化测试,以更深入地检验应用功能。为此,我们将使用一款名为 Selenium 的 Web 浏览器自动化工具。在这里我们将继续使用 ClipBucket 角色,但这些思路通常适用于任何使用 Ansible 部署的 Web 应用。

使用 curl 进行基础测试

在我们最初的构建脚本中,最后的测试如下所示:

curl -s "${container_ip}"  \
  | grep "Login" && \
  (echo 'Landing page test: pass' && exit 0) || \
  (echo 'Landing page test: fail' && exit 1)

这是一项有用的测试,因为它验证了我们 Web 应用的几个重要属性,即:

  • Web 服务器正在监听 80 端口
  • Web 服务器正在响应用户请求
  • Web 服务器正在提供 ClipBucket 首页

为什么我们需要更好的测试

尽管最初的测试提供了一些重要的检查,但它们几乎没有检验 Web 应用的实际功能。我们可能会在 Ansible 角色中引入一些会导致 Web 应用损坏的缺陷,却无法被基础测试发现。

例如,假设我们在 ClipBucket playbook 中不小心删除了一个关键任务:

diff --git a/tasks/main.yml b/tasks/main.yml
index 8542ffc..e9d42c0 100644
--- a/tasks/main.yml
+++ b/tasks/main.yml
@@ -29,15 +29,6 @@
     name: flvtool2
     state: present

-- name: create a symlink for ClipBucket to find ffmpeg and MP4Box
-  file:
-    path: "/usr/local/bin/{{ item }}"
-    src: "/usr/bin/{{ item }}"
-    state: link
-  with_items:
-    - ffmpeg
-    - MP4Box
-
 - name: install the Python MySQLB module
   pip: name=MySQL-python

如果我们使用这个修改后的 playbook 进行部署,然后浏览目标服务器,一切看起来都很正常:

ClipBucket 无错误提示

所有安装任务都成功执行,我们也看到了 ClipBucket 首页。问题出在哪里呢?

让我们尝试上传一个视频。一切正常,直到我们尝试查看它时:

视频错误截图

由于我们在 playbook 中删除了创建指向 ffmpeg 的符号链接的任务,ClipBucket 无法将视频转码为可流式播放的格式。

我们想为此创建一个自动化测试,但使用简单的命令行工具来编写上传视频的脚本非常困难。用户首先必须登录(这意味着脚本需要在多个请求之间管理 Cookie),然后还需要通过 Web 界面导航来上传视频。在一系列 curl 命令中完成这些操作会非常困难。

幸运的是,我们可以使用 Selenium。Selenium 是一款 Web 测试工具,可以让我们以编程方式执行 Web 浏览器操作。

配置 Selenium

要开始使用 Selenium,我们需要在 Ansible 控制机上安装几个组件:

  • Selenium Python 软件包 - 我们将使用 Python API,这个软件包提供了 Selenium 框架和 Python 绑定。
  • Firefox - 我们需要一个供 Selenium 驱动的浏览器。虽然 Selenium 支持大多数主流浏览器,但它原生支持 Firefox。
  • xvfb - 由于我们将在没有真实显示器的虚拟机上运行此测试,我们将使用 xvfb 作为虚拟显示器,让 Firefox 以为自己正运行在显示器上。

我们可以为此创建一个相当简单的 playbook。唯一有点困难的部分是 xvfb 需要一个不太直观的 init 脚本。幸运的是,博主 Cory Klein(科里·克莱因)去年写了一篇文章,给出一个 xvfb init 脚本的示例,参考他的示例,我们得以为我们的需求创建一个

选择要测试的 Web 流程

现在 Selenium 已经安装完毕,是时候创建一个 Selenium 脚本来检验我们的 Web 应用了。我们可能想要验证的 Web 流程有很多种,例如:

  • 登录
  • 上传视频并回放
  • 在视频下发表评论并检查其是否显示
  • 创建新用户账户

对于 ClipBucket,我特别关注视频能否正确上传,因此我们需要测试登录和视频上传。不幸的是,ClipBucket 使用了一个重量级的 JavaScript 软件包来管理上传,因此用于上传文件的常规 Selenium API 无法正常工作。

作为替代方案,我们将使用 ClipBucket 的模块诊断页面。该页面显示 ClipBucket 已安装的模块,并在任何模块未正确安装时会显示错误信息:

ClipBucket 模块视图

我们可以用这个页面来代替视频上传流程,以验证所有模块是否都已正确安装。

既然我们已经知道想要检验哪些功能,就可以草拟出想要自动化的 Web 流程。它大致如下所示:

  1. 加载 ClipBucket 网址
  2. 以管理员身份登录
  3. 进入模块诊断页面
  4. 验证所有模块均已安装

这将让我们能够对一些在基础测试中未曾检验的额外功能进行自动化验证:

  • 用户登录功能正常(这意味着 ClipBucket 可以成功访问数据库)
  • ClipBucket 可以访问其工具依赖

自动化 Web 流程

要在 Selenium 中自动化浏览器操作,我们需要告诉 Selenium 在浏览器中加载哪个 URL,然后向 Selenium 展示如何在页面上查找和交互元素。让我们来看看如何为期望的 Web 流程自动化所需的操作。

自动化登录

要登录,我们需要在登录页面上找到凭证字段,输入用户名和密码,然后点击“Login”按钮。幸运的是,用户名和密码字段都有 id 属性,这让我们在页面上识别它们变得非常容易。“Login”按钮没有 id 属性,但它有一个在页面上唯一的 name 属性 login,因此我们可以将其用作唯一标识符:

ClipBucket 登录字段

我们在下面的代码片段中定位这些字段并输入登录凭证:

self.get('/admin_area/login.php')

username_element = self._driver.find_element_by_id('username')
ui.WebDriverWait(
    self._driver, TIMEOUT).until(
    expected_conditions.visibility_of(username_element))
password_element = self._driver.find_element_by_id('password')

username_element.send_keys(username)
password_element.send_keys(password)
self._driver.find_element_by_name('login').click()

抓取模块列表

检查已安装模块页面的方式有些不同。我们根本不需要与页面交互;我们只需要查找并解析一些页面元素。我们想要确保所有模块都已正确安装,这意味着我们需要识别出标识每个模块的方框,然后以编程方式判断它们是否显示了错误信息。

这有点棘手,因为我们关注的元素(或它们在 DOM 中的父元素)都没有 id 属性。它们都是带有 class="well"<div>,所以这是我们用于查找每个模块信息框的最佳选择。

找到这些方框后,我们需要判断方框显示的是模块安装成功还是存在问题。我们可以通过寻找成功的标志,或验证元素是否缺少失败的标志来实现。前者更为严谨,但后者编码更简单。带有错误信息的方框始终包含一个带有 class="alert" 属性的元素,因此如果方框没有任何带有此类的子元素,我们就可以认定它是成功安装的。

ClipBucket 模块错误

我们可以通过下面的代码片段来实现这一点。

self.get('/admin_area/cb_mod_check.php')

for module_element in self._driver.find_elements_by_class_name('well'):
    try:
        ui.WebDriverWait(
                self._driver, TIMEOUT).until(
                expected_conditions.visibility_of(module_element))
        alert_element = module_element.find_element_by_class_name(
            'alert')
        if alert_element:
            raise ClipBucketModuleError(alert_element.text)
    except exceptions.NoSuchElementException:
        # Lack of alert is good: the module is installed correctly.
        continue

在 Travis 中集成 Web 流程测试

综合起来,我们可以将 Selenium 安装 playbook 和 Selenium 脚本添加到我们的构建文件中。

现在,我们使用之前有缺陷的提交来创建一个 GitHub 拉取请求。当 Travis 构建运行时,我们可以看到它失败了:

2016-09-21 00:17:06,229 clipbucket_driver INFO     Logging in with username=testadmin
2016-09-21 00:17:06,229 clipbucket_driver INFO     Loading url: http://172.17.0.2/admin_area/login.php
2016-09-21 00:17:07,525 clipbucket_driver INFO     Login complete
2016-09-21 00:17:07,526 clipbucket_driver INFO     Checking ClipBucket modules
2016-09-21 00:17:07,526 clipbucket_driver INFO     Loading url: http://172.17.0.2/admin_area/cb_mod_check.php
Traceback (most recent call last):
  File "tests/clipbucket_driver/main.py", line 36, in <module>
    main(parser.parse_args())
  File "tests/clipbucket_driver/main.py", line 24, in main
    driver.do_check_modules()
  File "/home/travis/build/mtlynch/ansible-role-clipbucket/tests/clipbucket_driver/clipbucket_driver.py", line 64, in do_check_modules
    raise ClipBucketModuleError(alert_element.text)
clipbucket_driver.ClipBucketModuleError: ffmpeg is not found

太好了!我们的新测试正确地识别出了 Ansible 角色中的问题。

让我们确保在运行正常的 playbook 时一切正常:

2016-09-21 01:58:21,992 clipbucket_driver INFO     Logging in with username=testadmin
2016-09-21 01:58:21,992 clipbucket_driver INFO     Loading url: http://172.17.0.2/admin_area/login.php
2016-09-21 01:58:23,036 clipbucket_driver INFO     Login complete
2016-09-21 01:58:23,039 clipbucket_driver INFO     Checking ClipBucket modules
2016-09-21 01:58:23,040 clipbucket_driver INFO     Loading url: http://172.17.0.2/admin_area/cb_mod_check.php
2016-09-21 01:58:23,724 clipbucket_driver INFO     Module check complete
2016-09-21 01:58:23,724 clipbucket_driver INFO     Exiting ClipBucket driver

Selenium 脚本正确地判断出所有必需的模块都已正确安装。如果将来我们所做的任何更改导致登录功能失效或模块安装不正确,自动化测试都会捕捉到。

结论

在这篇博文中,我们将 Ansible 与 Selenium 结合,验证了 Ansible 角色是否正确部署了 Web 应用,以及部署结果是否支持预期的用户行为。Selenium 提供了极大的灵活性,因此可以在各种 Web 应用中测试多种不同的 Web 界面流程。

原文由 Michael Lynch 发布

本文章由 muse-spark-1.2-contributor 进行翻译