利用Twill进行Web应用前端测试及集成系统测试
1. Twill语言基础命令
Twill 提供了一系列命令用于与 Web 页面进行交互和测试。以下是一些常用命令的介绍:
| 命令 | 功能 |
| — | — |
|
reset_output
| 撤销
redirect_output
的效果 |
|
reload
| 重新加载当前 URL,类似于普通浏览器的刷新按钮 |
|
reset_browser
| 销毁当前 Twill 会话的所有状态信息,相当于停止并重新启动 Twill |
|
run
| 执行任意 Python 语句,若语句包含空格需用引号括起来 |
|
runfile
| 执行存储在单独文件中的 Twill 脚本,脚本有自己的局部命名空间并共享全局命名空间 |
|
save_html
| 将当前页面的 HTML 内容保存到文件,可指定文件名,未指定则根据 URL 自动选择 |
|
show
| 打印当前页面的 HTML 内容,有助于了解 Twill 所见内容 |
|
showforms
| 打印当前页面所有表单的列表,包含表单编号、名称及各字段信息 |
|
showhistory
| 按时间顺序打印当前 Twill 会话中访问过的所有 URL |
|
showlinks
| 打印当前页面的链接列表,有助于确定
follow
命令的输入或用于调试 |
|
sleep
| 在 Twill 脚本执行中插入暂停,可指定暂停秒数,未指定则默认 1 秒 |
|
submit
| 提交
formvalue
命令最近更改字段所在的表单,可指定提交按钮 |
|
tidy_ok
| 若安装了 HTML Tidy,用于检查当前页面代码是否正确,不满足标准则脚本失败 |
|
title
| 接受正则表达式作为参数,尝试将当前页面标题与之匹配,不匹配则命令失败 |
|
url
| 接受正则表达式作为参数,尝试将当前页面 URL 与之匹配,不匹配则命令失败,匹配则绑定
__match__
变量 |
1.1 Twill命令使用示例
以下是一些使用上述命令的示例:
# 保存当前页面 HTML 内容
save_html('page.html')
# 打印当前页面链接
showlinks
# 暂停 2 秒
sleep 2
1.2 小测验
-
当使用
submit命令时,提交的是哪个表单? - 应该使用哪个命令来检查页面上是否没有错误消息?
- 当执行 Twill 脚本时,命令失败会发生什么?
1.3 实践操作
打开 Twill 交互式 shell,使用它搜索 Google,跟随搜索结果中的一个链接并在链接的网站中导航,同时尽可能多地使用 Twill 命令获取实践经验。
2. 从测试中调用Twill脚本
虽然可以使用
twill-sh
执行一系列 Twill 脚本进行自动化测试,但我们更希望将 Twill 脚本作为正常测试套件的一部分运行。有两种方法可以从 Python 代码中运行 Twill 脚本。
2.1 运行Twill脚本文件
步骤如下:
1. 将以下代码放入名为
fail.twill
的文件中:
go http://slashdot.org/
find this_does_not_exist
-
使用
twill-sh运行该脚本:
$ twill-sh fail.twill
- 在交互式 Python shell 中运行脚本:
>>> from twill.parse import execute_file
>>> execute_file('fail.twill')
这样就可以在 Python 代码中运行 Twill 脚本,并且
execute_file
会将 Twill shell 报告的错误作为
twill.errors.TwillAssertionError
异常抛出,便于与自动化测试工具集成。
2.2 运行Twill脚本字符串
步骤如下:
1. 打开交互式 Python 解释器并输入以下命令:
>>> from twill.parse import execute_string
>>> execute_string("""
... go http://slashdot.org/
... find this_does_not_exist
... """, no_reset = False)
注意
no_reset = False
参数,若省略,Twill 会假设所有
execute_string
调用都在同一个浏览器会话中执行,我们希望测试相互独立,所以需要该参数。而
execute_file
则默认相反,无需传递该参数。
2.3 巧妙技巧
如果使用 Python 2.4 或更高版本,可以定义一个函数装饰器,使编写 Twill 测试像编写 Python 函数一样简单:
from twill.parse import execute_string
from twill.errors import TwillAssertionError
def twill_test(func):
def run_test(*args):
try:
execute_string(func.__doc__, no_reset = False)
except TwillAssertionError, err:
if args and hasattr(args[0], 'fail'):
args[0].fail(str(err))
else:
raise
return run_test
使用示例:
from unittest import TestCase
from twill_decorator import twill_test
class web_tests(TestCase):
@twill_test
def test_slashdot(self):
"""
go http://slashdot.org/
find this_does_not_exist
"""
使用 Nose 或 unittest 运行该测试模块时,
test_slashdot
函数会自动执行其文档字符串中的 Twill 脚本,并将错误报告为测试失败。
3. 将Twill操作集成到unittest测试中
到目前为止,单元测试将每个 Twill 脚本视为一个产生成功或失败的单一操作。如果想下载 HTML 页面、对其内容与数据库的关系进行断言,然后跟随链接到另一个页面,可以直接从测试代码中访问 Twill 的浏览器对象。
3.1 使用Twill的浏览器对象
以下是一个使用浏览器对象的示例:
from unittest import TestCase
import twill
class test_twill_browser(TestCase):
def test_slashdot(self):
browser = twill.get_browser()
browser.go('http://slashdot.org/')
self.assertTrue(browser.get_code() in (200, 201))
html = browser.get_html()
self.assertTrue(html.count('slashdot') > 150)
link = browser.find_link('Science')
browser.follow_link(link)
form = browser.get_form(2)
form.set_value('aardvark', name = 'fhfilter')
browser.clicked(form, None)
browser.submit()
self.assertEqual(browser.get_code(), 200)
运行该测试模块:
nosetests
如果 Slashdot 界面未改变,测试将通过;否则可能失败。
3.2 浏览器对象方法
通过
twill.get_browser()
获取的浏览器对象有以下有用方法:
| 方法 | 功能 |
| — | — |
|
go
| 访问指定 URL |
|
reload
| 重新加载当前页面 |
|
back
| 返回上一页 |
|
get_code
| 返回当前页面的 HTTP 代码 |
|
get_html
| 返回当前页面的 HTML 内容作为 Python 字符串 |
|
get_title
| 返回当前页面的标题作为 Python 字符串 |
|
get_url
| 返回当前页面的 URL 作为 Python 字符串 |
|
find_link
| 搜索 URL、文本或名称匹配正则表达式的链接,找到则返回链接对象,未找到返回 None |
|
follow_link
| 跟随链接对象指向的地址,若为字符串 URL 则使用
go
方法 |
|
set_agent_string
| 设置用户代理字符串 |
|
get_all_forms
| 返回页面上所有表单对象的列表 |
|
get_form
| 接受正则表达式作为参数,搜索匹配的表单并返回表单对象 |
|
get_form_field
| 接受表单对象和正则表达式作为参数,返回匹配的表单控件对象 |
|
clicked
| 告知浏览器对象当前焦点所在,用于确定提交表单时的表单对象 |
|
submit
| 提交最后点击的表单,可指定提交按钮 |
|
save_cookies
| 保存 cookies 到文件 |
|
load_cookies
| 从文件加载 cookies |
|
clear_cookies
| 清除 cookies |
3.3 方法详细说明
- get_code :仅返回当前页面的 HTTP 代码,不进行代码与预期值的比较,若要在代码不为 200 时抛出异常需自行处理。
- get_html :返回当前页面的 HTML 内容作为 Python 字符串。
- get_title :返回当前页面的标题作为 Python 字符串。
- get_url :返回当前页面的 URL 作为 Python 字符串。
-
find_link
:搜索匹配正则表达式的链接,链接对象有
attrs、text和absolute_url等属性。 -
follow_link
:跟随链接对象指向的地址,若为字符串 URL 则使用
go方法。 -
get_all_forms
:返回页面上所有表单对象的列表,若有不在
<form>标签内的表单控件,会创建特殊表单对象作为列表第一个元素。 -
get_form
:接受正则表达式作为参数,搜索匹配的表单并返回表单对象,表单对象有
name、method、action、enctype和attrs等属性,还有get_value、set_value等方法用于操作表单控件。 -
get_form_field
:接受表单对象和正则表达式作为参数,返回匹配的表单控件对象,主要用于为
clicked方法提供输入。 - clicked :告知浏览器对象当前焦点所在,用于确定提交表单时的表单对象,可传入表单对象和特定控件对象,若控件为提交控件则成为新的默认提交控件。
- submit :提交最后点击的表单,可指定提交按钮。
3.4 小测验
-
调用
get_form方法时,如何指定要检索的表单对象? -
clicked方法的作用是什么? -
get_code方法与code命令有何不同?
4. 总结
通过本章学习,我们了解了 Twill 语言及其在 Web 应用测试中的使用方法,包括如何从 Python 测试中调用 Twill 脚本,以及如何将 Twill 操作集成到 unittest 测试中。利用 Twill 的浏览器对象,我们可以更灵活地与 Web 页面进行交互和测试。掌握这些知识后,我们可以更有效地对 Web 应用进行前端测试。
5. 集成测试和系统测试
到目前为止,我们主要关注的是单元测试,即对代码中最小可测试单元的测试。现在是时候扩大范围,开始测试包含多个单元的代码了。
5.1 集成测试和系统测试的概念
集成测试是检查程序中各个单元是否能正确协同工作的过程。在单元测试确保各个单元正常工作后,进行集成测试是必要的,因为单元之间的交互可能会产生意想不到的问题。如果直接从集成测试开始,当出现问题时很难追踪原因。
5.2 本章目标
- 描述集成测试和系统测试
- 学习如何将程序分解为可测试的多单元部分
- 使用 doctest、unittest 和 Nose 自动化多单元测试
5.3 集成测试的重要性
集成测试可以发现单元之间接口的问题,确保各个单元组合在一起后能正常工作。例如,一个模块可能在单独测试时表现良好,但与其他模块集成时可能会出现数据传递错误或功能冲突的问题。通过集成测试,可以及时发现并解决这些问题,提高软件的整体质量。
5.4 实践操作
以下是一个简单的集成测试示例,假设我们有两个函数
add
和
multiply
:
def add(a, b):
return a + b
def multiply(a, b):
return a * b
# 集成测试示例
result = multiply(add(2, 3), 4)
assert result == 20
在这个示例中,我们先调用
add
函数,然后将结果作为参数传递给
multiply
函数,最后检查最终结果是否符合预期。
5.5 总结
集成测试和系统测试是软件开发过程中不可或缺的环节,它们可以帮助我们发现单元之间的交互问题,确保软件的整体功能正常。通过使用合适的测试工具和技术,我们可以自动化多单元测试,提高测试效率和准确性。在后续的开发中,我们应该重视集成测试和系统测试,以确保软件的质量和稳定性。
6. 集成测试的策略与方法
6.1 自顶向下集成测试
自顶向下集成测试是从系统的顶层模块开始,逐步向下集成子模块的测试方法。其基本步骤如下:
1. 选择一个顶层模块作为起始点。
2. 对该顶层模块进行单独测试。
3. 逐步将其直接下层的子模块集成进来,并进行测试。
4. 重复步骤 3,直到所有模块都被集成并测试完毕。
以下是一个简单的自顶向下集成测试的 mermaid 流程图:
graph TD;
A[顶层模块测试] --> B[集成第一层子模块];
B --> C[测试集成后的模块];
C --> D{是否还有未集成子模块};
D -- 是 --> B;
D -- 否 --> E[测试完成];
6.2 自底向上集成测试
自底向上集成测试则是从系统的底层模块开始,逐步向上集成的测试方法。具体步骤如下:
1. 确定所有底层模块。
2. 对每个底层模块进行单独测试。
3. 将底层模块组合成更大的模块,并进行测试。
4. 重复步骤 3,直到所有模块都被集成到顶层模块并测试完毕。
对应的 mermaid 流程图如下:
graph TD;
A[底层模块测试] --> B[组合底层模块成更大模块];
B --> C[测试组合后的模块];
C --> D{是否到达顶层模块};
D -- 否 --> B;
D -- 是 --> E[测试完成];
6.3 混合集成测试
混合集成测试结合了自顶向下和自底向上集成测试的优点。可以先使用自顶向下的方法集成一部分模块,再使用自底向上的方法集成另一部分模块,最后将两部分集成在一起进行测试。这种方法可以根据系统的特点和需求灵活选择测试顺序,提高测试效率。
7. 系统测试的类型与重点
7.1 功能测试
功能测试是系统测试的核心,主要验证系统的各项功能是否符合需求规格说明书的要求。测试人员需要根据需求文档编写测试用例,覆盖系统的各种功能场景。例如,对于一个电商系统,功能测试可能包括商品搜索、购物车添加、订单结算等功能的测试。
7.2 性能测试
性能测试关注系统在不同负载下的性能表现,包括响应时间、吞吐量、并发用户数等指标。常见的性能测试类型有负载测试、压力测试和并发测试。以下是一个简单的性能测试指标表格:
| 测试类型 | 测试目的 | 关注指标 |
| — | — | — |
| 负载测试 | 测试系统在正常负载下的性能 | 响应时间、吞吐量 |
| 压力测试 | 测试系统在极限负载下的性能 | 系统稳定性、资源利用率 |
| 并发测试 | 测试系统在多个用户同时访问时的性能 | 并发用户数、响应时间 |
7.3 安全测试
安全测试旨在发现系统中的安全漏洞,确保系统的安全性。测试内容包括身份验证、授权、数据加密、漏洞扫描等方面。例如,对于一个在线银行系统,安全测试需要验证用户登录的身份验证机制是否可靠,以及用户数据在传输和存储过程中是否加密。
7.4 兼容性测试
兼容性测试主要检查系统在不同环境下的兼容性,包括操作系统、浏览器、数据库等。例如,一个 Web 应用需要在不同版本的 Chrome、Firefox 等浏览器上进行兼容性测试,确保用户在各种浏览器上都能正常使用。
8. 使用 doctest、unittest 和 Nose 进行自动化多单元测试
8.1 doctest 的使用
doctest 是 Python 内置的一个测试框架,它可以从文档字符串中提取测试用例并执行。以下是一个使用 doctest 的示例:
def add(a, b):
"""
This function adds two numbers.
>>> add(2, 3)
5
"""
return a + b
import doctest
doctest.testmod()
在这个示例中,文档字符串中的
>>> add(2, 3)
是一个测试用例,
5
是预期结果。doctest 会自动执行这个测试用例,并检查结果是否符合预期。
8.2 unittest 的使用
unittest 是 Python 标准库中的另一个测试框架,它提供了更丰富的测试功能。以下是一个使用 unittest 的示例:
import unittest
def add(a, b):
return a + b
class TestAdd(unittest.TestCase):
def test_add(self):
result = add(2, 3)
self.assertEqual(result, 5)
if __name__ == '__main__':
unittest.main()
在这个示例中,我们定义了一个
TestAdd
类,继承自
unittest.TestCase
,并在其中定义了一个测试方法
test_add
。使用
self.assertEqual
方法来检查结果是否符合预期。
8.3 Nose 的使用
Nose 是一个第三方测试框架,它可以自动发现和运行测试用例。使用 Nose 非常简单,只需要安装 Nose 并在项目根目录下运行
nosetests
命令即可。以下是一个简单的项目结构示例:
project/
├── module.py
└── test_module.py
module.py
中定义了要测试的函数:
def add(a, b):
return a + b
test_module.py
中定义了测试用例:
from module import add
def test_add():
result = add(2, 3)
assert result == 5
在项目根目录下运行
nosetests
命令,Nose 会自动发现并运行
test_module.py
中的测试用例。
9. 将程序分解为可测试的多单元部分
9.1 模块化设计
模块化设计是将程序分解为多个独立的模块,每个模块具有明确的功能和接口。这样可以提高代码的可维护性和可测试性。例如,一个 Web 应用可以分为用户管理模块、商品管理模块、订单管理模块等。
9.2 接口设计
良好的接口设计可以降低模块之间的耦合度,使得每个模块可以独立开发和测试。接口应该清晰地定义输入和输出,以及模块之间的交互方式。例如,一个数据处理模块可以定义一个接口,接受特定格式的数据作为输入,并返回处理后的数据。
9.3 依赖管理
在将程序分解为多个模块后,需要管理模块之间的依赖关系。可以使用工具如 pip 来管理 Python 包的依赖,确保每个模块可以正确地引用所需的其他模块。
以下是一个简单的程序分解示例:
web_app/
├── user_management/
│ ├── __init__.py
│ ├── user_model.py
│ ├── user_service.py
│ └── test_user_service.py
├── product_management/
│ ├── __init__.py
│ ├── product_model.py
│ ├── product_service.py
│ └── test_product_service.py
└── order_management/
├── __init__.py
├── order_model.py
├── order_service.py
└── test_order_service.py
在这个示例中,
web_app
被分解为三个模块:
user_management
、
product_management
和
order_management
,每个模块都有自己的模型、服务和测试文件。
10. 总结与展望
10.1 总结
通过本文的学习,我们深入了解了 Twill 在 Web 应用前端测试中的使用,包括 Twill 语言的基础命令、如何从 Python 测试中调用 Twill 脚本以及将 Twill 操作集成到 unittest 测试中。同时,我们也学习了集成测试和系统测试的概念、策略与方法,以及如何使用 doctest、unittest 和 Nose 进行自动化多单元测试。将程序分解为可测试的多单元部分是提高软件可维护性和可测试性的重要方法。
10.2 展望
在未来的软件开发中,随着 Web 应用的复杂性不断增加,对测试的要求也会越来越高。我们需要不断探索和应用新的测试技术和工具,以提高测试效率和准确性。例如,使用自动化测试框架进行持续集成和持续交付,确保软件在开发过程中始终保持高质量。同时,结合人工智能和机器学习技术,实现智能测试,自动发现软件中的潜在问题。通过不断学习和实践,我们可以更好地应对软件开发中的各种挑战,确保软件的质量和稳定性。
超级会员免费看
37

被折叠的 条评论
为什么被折叠?



