使用 Selenium 测试 Ansible Web 应用角色
原文由 Michael Lynch 于 发布,订阅该博客
概述
Ansible 是部署 Web 应用的优秀工具。通过 Ansible,我们可以将 Web 应用定义为组成它的不同“角色”(例如 Web 服务器、数据库服务器、应用服务器)。随着角色及其交互变得越来越复杂,我们需要更强大的测试手段来验证 Web 应用是否被正确部署。
在上一篇文章中,我们使用 Ansible 部署了一个名为 ClipBucket 的 Web 应用,这是一个视频托管应用。在那篇文章中,我们加入了自动化测试来验证 Web 应用是否正确安装,但这些测试并没有对应用功能进行严格的检验。
在本文中,我们将展示更强的自动化测试,能够更深入地检验应用功能。为此,我们将使用一款名为 Selenium 的浏览器自动化工具。本文仍将以 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 的首页。那问题出在哪里呢?
我们来尝试上传一个视频。在尝试播放之前,一切似乎都正常:
由于我们在 playbook 中删除了为 ffmpeg 创建软链接的任务,ClipBucket 无法将视频转码为可播放的格式。
我们希望为此创建一个自动化测试,但用简单的命令行工具来编写上传视频的脚本非常困难。用户首先必须登录(这意味着脚本需要在多次请求之间管理 Cookie),然后还需要通过 Web 界面来上传视频。用一系列 curl 命令来完成这些操作会非常困难。
好在我们可以使用 Selenium。Selenium 是一款 Web 测试工具,可以让我们以编程方式执行浏览器操作。
搭建 Selenium 环境
要在 Ansible 控制机上开始使用 Selenium,我们需要安装几个组件:
- Selenium Python Package - 我们将使用 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 已安装的模块,并在某个模块未正确安装时显示错误信息:
我们可以用这个页面来代替视频上传流程,以验证所有模块是否都已正确安装。
明确了想要检验的功能后,我们可以梳理出要自动化的 Web 流程,大致如下:
- 加载 ClipBucket 的 URL
- 以管理员身份登录
- 进入模块诊断页面
- 验证所有模块均已安装
这将让我们能够自动验证一些在基础测试中未覆盖的额外功能:
- 用户登录功能正常(这意味着 ClipBucket 能够成功访问数据库)
- ClipBucket 能够访问其依赖的工具
自动化 Web 流程
要在 Selenium 中自动化浏览器操作,我们需要告诉 Selenium 在浏览器中加载哪个 URL,然后指明如何查找页面元素并与之交互。下面我们来看一下如何为所需的 Web 流程自动化这些操作。
自动化登录
要完成登录,我们需要在登录页面上找到凭证输入框,输入用户名和密码,然后点击“Login”按钮。好在用户名和密码输入框都有 id 属性,因此在页面上很容易定位。“Login”按钮没有 id 属性,但它有一个在页面上唯一的 name 属性,值为 login,我们可以用它作为唯一标识符:
我们在下面的代码片段中定位这些字段并输入登录凭证:
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" 属性的元素,因此如果某个方框没有任何带有该类的子元素,就可以认为它是安装成功的。
我们可以通过下面的代码片段来实现。
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 pull request。当 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 driverSelenium 脚本正确地判断出所有必需的模块都已正确安装。如果将来我们做了某些改动导致登录功能失效或模块安装不正确,自动化测试就会捕捉到这些问题。
结论
在本文中,我们将 Ansible 与 Selenium 结合起来,验证了 Ansible 角色是否正确部署了 Web 应用,以及部署结果是否支持预期的用户行为。Selenium 提供了极大的灵活性,因此可以用来在各种 Web 应用中测试多种不同的 Web 界面流程。
随机一篇博客





评论
登录后参与讨论