小编使用Lettuce、PO(非PO)模式写的UI自动化测试框架可供参考地址
https://github.com/Mrfenghaha/DemoUITestLettuce
一、Lettuce介绍
1.1 BDD
了解lettuce需要先了解BDD,BDD是TDD的一种衍生,通过特定的BDD框架,用自然语言或类自然语言,按照编写用户故事或者用户用例的方式,以功能使用者的视角,描述并编写测试用例。
BDD源于TDD并优于测试驱动开发。
之所以说BDD优于测试驱动开发,并非空穴来风,主要原因如下:
- 更加以人为本:TDD更多关注于测试接口实现逻辑正确性,而BDD重点关注用户使用功能时的行为和结果是否与符合预期。
- 更加以人为本:TDD基本上是使用编程语言来描述测试用例,而BDD则是用自然语言来描述测试用例。
- 更加以人为本:TDD不关注客户价值,而BDD从客户价值开始书写
- 更加以人为本:TDD的需求文档和测试用例是分别存储的,而BDD的需求文档就是测试用例
- 更加以人为本:TDD要求所有被覆盖的接口都要进行良好重构,而BDD只要求对暴露给客户使用的接口甚至UI具有可测试性
- 更加以人为本:TDD更多是团队纪律或者领导推动的,而BDD是客户需求拉动的
1.2 Lettuce
学习BDD(行为驱动开发),业界流行的BDD框架是Ruby语言编写的Cumumber,不过对于用Python习惯的朋友可以学习一下Cumumber在Python下的衍生品-Lettuce。
lettuce 除了官方文档外,几乎找不到其它资料,为了理解lettuce,我们不妨多去看看cucumber 的资料。对于项目的自动化测试,它可以执行纯文本的功能描述,它允许我们用自然语言去描述个一个系统的行为。
可以学习别人分享的官网文档翻译博客
二、Lettuce安装
2.1 Python2
命令行安装(当前的lettuce0.2.x版本仍然只支持python2)
pip install Lettuce
2.2 Python3
- 方式一
因为lettuce目前只支持python2,python3需要自行下载安装。Github有大佬更新的0.2.23最新版本
https://github.com/sgpy/lettuce/tree/py3
git clone https://github.com/sgpy/lettuce.git # clone大佬源码在任意位置
cd lettuce # 进入源码项目
git checkout -b py3 origin/py3 # 创建python3分支并clone
sudo python3 setup.py install # 安装支持python3的版本替换原lettuce
lettuce --version # 检查版本号看是否安装成功
此方法是直接安装lettuce但是需要加sudo授权
- 方式二(使用旧版本替换)
Github有大佬更新的0.2.10版本
https://github.com/e0ne/lettuce/tree/python3
pip3 install lettuce # 安装lettuce
git clone https://github.com/e0ne/lettuce.git # clone大佬源码在任意位置
cd lettuce # 进入源码项目
git checkout -b python3 origin/python3 # 创建python3分支并clone
python3 setup.py install --record logName # 安装支持python3的版本替换原lettuce,并且将文件路径记录下载方便卸载删除
lettuce -V # 检查版本号是否安装成功
此方法只是替换了lettuce的访问路径,调用的时候路径发生变化,但是pip3安装的版本仍在
2.3 卸载
python2 直接卸载即可
pip uninstall lettuce
python3方式一直接卸载即可
sudo pip3 uninstall lettuce # 需要加sudo因为安装极为sudo权限
python3 方式二卸载麻烦一些
pip3 uninstall lettuce # 卸载pip安装的lettuce版本
cd lettuce
cat logName | xargs rm -rf # 根据安装的log路径删除替换版本相关文件,这种方法可能删除的不干净
or
sudo rm -rf /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/lettuce/ # 直接删除整个文件
sudo rm -rf /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/lettuce-0.2.23-py3.7.egg-info # 删除信息文件
三、Lettuce使用
3.1 项目文件结构介绍
|-- features # lettuce 会根据目录路径查找相关文件
| -- xxxx.feature # BDD 的描述文件
| -- xxxx.py # 相关的python方法脚本
可以根据自己的需要在features下再创建文件夹用于分类等,lettuce会自动读取featuress文件下所有文件
3.2 使用介绍
3.2.1 样例
sample.feature
Feature: Compute factorial
In order to play with Lettuce
As beginners
We'll implement factorial
Scenario: Factorial of 0
Given I have the number 0
When I compute its factorial
Then I see the number 1
sample.py
# -*- coding:utf-8 -
from lettuce import *
def factorial(number):
return 1
@step('I have the number (\d+)')
def have_the_number(step, number):
world.number = int(number)
@step('I compute its factorial')
def compute_its_factorial(step):
world.number = factorial(world.number)
@step('I see the number (\d+)')
def check_number(step, expected):
expected = int(expected)
assert world.number == expected, "Got %d" % world.number
3.2.2 描述文件
lettuce规定了一系列的语法,你按照它的规则 告诉它,它就可以理解你的需求。feture文件中的关键字的作用和单元测试中的关键字的对应关系如下:
3.2.3 world
为了更容易和更有趣的编写测试,Lettuce”违背了“python中好的设计的一些原则,如避免隐含的利用全局概念。
Lettuce的“world”概念主要是“全局的概念”。例如下面UI自动化测试的例子,启动浏览器将driver写入world中,后续的调用直接引用world.driver即可。
3.2.4 脚本文件
steps里面都是使用@step这个注解来表示你要做的行为对应的代码实现。
对于内容替换则按照正则表达式的方式,将函数所需的参数直接按照顺序提取出来
3.2.5 执行
直接在根目录下执行lettuce即可,即features上一层文件夹执行
lettuce # 执行所有的测试用例
lettuce /xx/xx/xx/xxx.feature # 执行指定文件,这种方式可以不将features用例文件放在features文件下
3.2.6 结果![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/af6787078c7d1a587ee4ae72ec6abc7a.png)
3.3 进行接口测试
interface.feature
Feature: Interface Learning
In order to play with Lettuce
As beginners
Scenario: Test Interface
Given I have the parm 10 and 0
When I to calling interface
Then I see the state of response is 200
Then I see the success of response is "true"
interface.py
# -*- coding:utf-8 -
import json
import requests
from lettuce import *
# string类型转布尔值类型
def str_to_boolean(value):
return value == 'true'
def mock_show_lists(page_num, num):
request = {
"url": 'http://localhost:5000/mock_server/mock/show_lists',
"method": "post",
"headers": {"content-type": "application/json"},
"data": {"page_num": page_num, "num": num}
}
return requests.post(url=request['url'], data=json.dumps(request['data']), headers=request['headers'])
@step('I have the parm (\d+) and (\d+)')
def have_the_parm(step, page_num, num):
world.number = int(page_num)
world.num = int(num)
@step('I to calling interface')
def calling_interface(step):
world.result = mock_show_lists(world.number, world.num)
@step('I see the state of response is (\d+)')
def check_number(step, status):
world.status = int(status)
assert world.result.status_code == world.status
@step('I see the success of response is "(.*?)"')
def check_number(step, success):
world.success = str_to_boolean(success)
assert world.result.json()['success'] == world.success
3.4 进行UI测试
testui.feature
Feature: Lettuce Learning
In order to play with Lettuce
As beginners
Scenario: Test Interface
Given I open the browser
When I Access to web sites "https://www.amazon.cn/"
Then I enter "toothbrush" in the search input box
Then I hit the search button
Then I close the browser
testui.py
# -*- coding:utf-8 -
from lettuce import *
from selenium import webdriver
from selenium.webdriver.common.by import By
@step('I open the browser')
def get_driver(step):
world.driver = webdriver.Chrome()
# 将浏览器窗口最大化
world.driver.maximize_window()
world.driver.implicitly_wait(5)
# return driver
@step('I Access to web sites "(.*?)"')
def visit_website(step, url):
# world.driver = get_driver()
world.driver.get(url)
@step('enter "(.*?)" in the search input box')
def send_search_action(step, text):
search_type = (By.ID, 'twotabsearchtextbox')
world.driver.find_element(*search_type).send_keys(text)
@step('I hit the search button')
def click_submit_action(step):
submit_type = (By.XPATH, '//*[@id="nav-search"]/form/div[2]/div/input')
world.driver.find_element(*submit_type).click()
@step('I close the browser')
def close(step):
world.driver.quit()
3.5 设置语言
Lettuce目前支持18种语言:
英语、葡萄牙(Português)、波兰(Polski)、加泰罗尼亚(Català)、西班牙(Español)、匈牙利(Magyar)、法国(Français)、德语(Deutsch)、日本(日本語)、土耳其(Türkçe)、简体中文(简体中文)、繁体中文(繁體中文)、俄罗斯(Русский)、乌克兰(Українська)、意大利(Italiano)、挪威(Norsk)、瑞典(Svenska)、捷克(Čeština)
具体的写法语言请参考源码,查看如图位置language .py文件即可
cat lettuce/lettuce/languages.py
设置为中文,只需要在每个features文件前加上# language: zh-CN,对应的step中的描述换为中文即可,例如下:
# language: zh-CN
特性: UI自动化测试模版
场景: 亚马逊商城搜索商品并加入购物车,检验是否添加成功以及金额
Given 我打开谷歌浏览器
And 我访问亚马逊首页
When 我在首页,搜索商品"牙刷"
And 我在商品搜索列表页,点击商品"惠百施 熊本熊 成人牙刷 (颜色随机)"
And 我切换到浏览器第2个页签
And 我在商品详情页,点击加入购物车按钮
And 我在商品加入购物车成功页面,获取成功文本
And 我比较文本与"商品已加入购物车"是否相等
And 我在商品加入购物车成功页面,获取金额文本
And 我比较文本与"¥ 81.88"是否相等
Then 我关闭浏览器或设备
3.6 持续集成Jenkins
集成Jenkins也很简单,只需要创建一个任务添加构建Execute shell即可,写入如下:
#!/bin/bash -ilex # 说明使用系统环境变量配置
cd xxxxx/xxxx/xxxxxx/xxxxx # cd到项目根目录
lettuce # 执行用例
问题
无法实现在py脚本中import其他自定义包,不利于封装维护。还未解决此问题,不知道是因为版本替换的原因,还是lettuce本身的特征(怀疑是由于world的机制造成了这种现象,不过可以好好利用world机制)