ython实现Web UI自动化测试需要具备的技能
自动化测试简介
练习神器:http://sahitest.com/demo
概念
自动化测试:人为驱动的测试行为转化为机器执行的一种过程。
自动化测试分类:
单元自动化测试(代码)
接口自动化测试(接口)
UI自动化测试(UI页面)
场景适用:
功能比较稳定的项目的:冒烟测试、缺陷回归测试、业务场景覆盖测试
UI自动化测试
UI自动化测试脚本思路
- 定位页面元素
- 对元素执行操作
- 自动检查结果
Selenium简介
特点
- 免费开源
- 支持多浏览器-IE、FireFox、Chrome、Safari
** 组成部分**
- Selenium WebDriver
属于核心部分,提供各种各样的接口,供用户实现Web UI自动化测试的功能 - Selenium IDE
可以录制和回放浏览器操作,从而快速创建自动化测试 - Selenium Grid
自动化脚本分发到不同的测试机器上进行执行
WebDriver组件
WebDriver通过Brower Driver与浏览器进行通过,并且以同样的路径来接收浏览器返回的信息
缺点
webDriver擅长配合Brower-Driver与浏览器进行通讯,但是不擅长如何比事物,如何断言,所以我们要使用各种框架来解决,本书选择Python语言作为开发语言,使用Unittest和Pytest作为框架,测试框架负责执行WebDriver中与测试相关的步骤
测试环境搭建
Python
下载地址:https://www.python.org/downloads/windows/
Doc:存放python帮助文档的文件夹
Lib:将来安装第三方库都会存放在该文件夹下
libs:内置库(可以直接引用的模块)
Scripts:包含可执行的文件
Selenium
- 安装selenium:pip install selenium
- 安装WebDriver:下载并且放到任一目录进行环境变量配置或者放到python路径下即可
Pycharm
提高程序开发时的效率
下载地址:https://www.jetbrains.com/pycharm/download/#section=windows
? 第三方包
? 具体详情操作Pycharm为准
Python知识
Python基本数据类型
数值 字符串 列表 字典 集合
顺序、分支、循环语句
Python函数、模块
Python类、实例
文件I/O操作
json和yaml文件
面向对象编程
OOP:object Oriented programming;属于一种设计思想,把计算机程序视为一组对象的集合,每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序执行过程就是一系列消息在各个对象之间传递的过程。
前端知识储备
HTML基础知识
参考菜鸟教程中HTML知识即可
CSS相关知识
CSS是层叠样式表,定义如何展示HTML元素
JavaScript相关知识
在实施自动化测试过程中我们需要借助JavaScript来操作页面元素,常用于web应用开发,为页面添加各式各样的动态功能
Selenium基础方法
selenium常用方法
driver = webdriver.Chrome()
driver.get('http://baidu.com')
driver.maximize_window()
driver.title
对于多窗口操作需要获取窗口的唯一标识:句柄
driver.current_window_handle
all = driver.window_handles
driver.switch_to.window(all[0])
Selenium元素定位方法
常用元素定位方法
鼠标操作
ele=driver.element_by_link_text('新闻')
ele.click()
from selenium.webdriver.common.action_chains import ActionChains
ActionChains(driver).context_click(ele).perform()
ActionChains(driver).move_to_element(ele).perform()
ActionChains(driver).drag_and_drop(source,target).perform()
Unitest框架
unittest四个重要的概念
测试固件 测试之前或者之后做一些操作,常用的test fixture有:setup、teardown、setupclass、teardownclass 测试用例 测试用例是unittest执行测试的最小单元,它通过unittest提供的assert方法验证特定的操作或者以后得到的具体响应 测试用例定义非常简单,如何合理地组织用例:
- 多个测试用例文件尽量不要存在依赖关系,因为一旦被依赖的测试用例执行失败,后续有依赖关系的测试用例也会失败
- 一个测试用例可以添加多个断言进行检查
测试套件 一组测试用例,作用是将多个测试用例放在一起,执行一个测试套件就可以将这些测试用例全部执行 测试运行器 测试运行器用来执行测试用例,并且返回测试用例的执行结果,可以使用图形、表格、文本等方式将测试结果形象地展现出来,HTMLTestRunner
import unnitest
class TestStorm(unittest.TestCase):
def setUp(self):
print('setup')
def test_first(self):
self.assertEqual('storm','storm')
def tearDown(self):
print ('teardown')
if __name__ =='__main__':
unittest.main()
测试固件的说明顺序
import unittest
class TestStorm(unittest.TestCase):
@classmethod
def setUpClass(cls):
print ('setUpClass')
def setUp(self):
print ('setup')
def test_first(self):
print ('first')
def test_second(self):
print ('second')
def tearDown(self):
print ('tearDown')
@classmethod
def tearDownClass(cls):
print ('tearDownClass')
if __name__ == '__main__':
unittest.main()
setUpClass
setup
first
tearDown
setup
second
tearDown
tearDownClass
unittest框架解决的问题
####### 测试用例的相关问题 unittest参数化也叫做数据驱动
对于测试用例来讲,如果是登录模块,我们要验证登录成功或登录失败,两个测试用例,两个的测试用例步骤其实是一样的,只不过传递的参数不一样,如果我们想降低代码的冗余性,可以讲脚本中的数据抽离出来,实现数据和代码的分离 unittest不支持参数化,我们需要借助第三方插件来实现 unittest+DDT
C:\Users\Flysky>pip3 install ddt
from selenium import webdriver
import unittest
import ddt
@ddt.ddt
class TestLogin(unittest.TestCase):
def setUp(self):
self.driver=webdriver.Chrome()
self.driver.maximize_window()
self.driver.implicitly_wait(20)
self.driver.get("http://localhost:8080/redmine/login")
'''
1,使用ddt进行传递参数
'''
@ddt.data(['admin','123456','0'],['admin','000000','1'])
@ddt.unpack
def test_login(self,username,password,status):
login_name=self.driver.find_element_by_id("username")
login_name.clear()
login_name.send_keys(username)
login_pwd=self.driver.find_element_by_id("password")
login_pwd.clear()
login_name.send_keys(password)
login_btn=self.find_element_by_id('login-submit')
login_btn.click()
if status =='0':
ele=self.driver.find_element_by_id('flash_error')
self.assertIn('无效的用户名',self.driver.page_source)
if status =='1':
name=self.driver.find_element_by_link_text(username)
self.assertEqual(name.text,username)
else:
print ('参数化的状态只能为0或者1')
def tearDown(self):
self.driver.quit()
if __name__=='__main__':
unittest.main()
?
PO设计模式
项目的易变性导致测试用例的维护成本越来越高,那我们使用PO设计模式来解决这个问题 开发可维护性高的测试脚本,对于自动化测试持续集成非常重要,如何去解决这些问题呢? PO:page object是指页面元素的定位以及元素的操作分离,测试用例直接调用这些封装好的元素操作来组织用例,从而实现测试用例脚本和元素定位、操作的分离 虽然PO思想被广大测试同行认可,但是不同团队在项目实践过程中采用了不同的分层模式
PO+pytest+Selenium
整体的规划:将系统按页面分成三层结构
-
元素对象层: 封装定位元素的方法 -
元素操作层: 借助元素对象层封装的操作方法 -
业务场景层 借助元素操作层封装当前页面的业务场景
|--------Package |----------------pageobject |-----------------------login_page.py |----------------report |----------------testcase
|----------------------test_001_login.py
对于login_page.py定义“查找元素类”
class LoginPage(object):
def __init__(self,driver):
self.driver=dirver
def find_username(self):
ele=self.driver.find_element_by_id('username')
return ele
def find_password(self):
ele = self.driver.find_element_by_id('password')
return ele
def login_btn(self):
ele = self.driver.find_element_by_id('login-submit')
return ele
def find_login_name(self):
ele = self.driver.find_element_by_id('loggedas')
return ele
def find_login_failed_info(self):
ele= self.driver.find_element_by_id('flash_error')
return ele
class LoginOper(object):
def __init__(self,driver)
self.login_page =LoginPage(driver)
def input_username(self,username):
user = self.login_page.find_username()
user.clear()
user.send_keys(username)
def input_password(self,password):
pwd = self.login_page.find_password()
pwd.clear()
pwd.send_keys(password)
def click_login_btn(self):
self.login_page.find_login_btn().click()
def get_login_name(self):
retrun self.login_page.find_login_name().text
def get_login_failed_info(self):
return self.login_page.find_login_failed_info().text
class LoginScenario(object):
def __init__(self,driver):
self.login_oper=LoginOper(driver)
def login(self,username,password):
self.login_oper.input_username(username)
self.login_oper.input_password(password)
self.login_oper.click_login_btn()
重构测试用例
test_001_login.py
from selenium import webdriver
import pytest
from package.pageobject import login_page
data=[('admin','123456','0'),('admin','000000','1')]
@pytest.mark.parametrize(("username","password","status"),data)
class TestLogin():
def setup(self):
self.driver=webdriver.Chrome()
self.driver.maximize_window()
self.driver.implicitily_wait(20)
self.driver.get("http://localhost:8080/resouce/login")
def tearDown(self):
self.driver.quit()
def test_001_login(self,username,password,status):
if status =='0':
text=login_page.LoginOper(self.driver).get_failed_info()
assert text=='无效的用户名或密码'
elif status =='1':
text = login_page.LoginOper(self.driver).get_login_name()
assert usernmae in text
else:
print ("参数化的状态只能传入0或1")
测试数据分离
实际工作中遇到两种常见情况:
- 同一场景需要增加删除修改数据以覆盖期望的测试场景
- 测试数据需要修改,例如之前的账号被同事删除了的场景
测试配置分离
Selenium API封装
二次封装的目的是简化一些复杂的操作,但是千万不要为了封装而封装,因为新封装的方法名对于其他项目的人员来说是比较陌生的。
Base目录下新建一个base.py文件
from selenium.webdriver.support.ui import WebDriverWait
class Base(object):
def __init__(self,driver):
self.driver=driver
def split_locator(self,locator):
if len(locator.split(',')) ==3:
by = locator.split(',')[0]
value=locator.split(',')[1]+','+locator.split(',')[2]
else:
by = locator.split(',')[0]
value=locator.split(',')[1]
locator_dict={
'id':'id',
'name':'name',
'tag':'tag name'
}
if by not in locator_dict.keys():
raise NameError("Not can be used")
return locator_dict[by],value
def get_element(self,locator,sec=20):
by,value=self.split_locator(locator)
try:
element=WebDriverWait(self.driver,sec,1).until(lambda x:x.find_element(by=by,value=value))
return element
测试计划
不同的项目有不同的目标,自然也就有不同的自动化测试计划,在做自动化测试计划时,我们需要搞明白以下几个问题:
- 项目是全新的项目还是比较稳定的项目
- 项目的开发模式是传统瀑布还是快速迭代,对于快速迭代的项目则要求测试团队对版本做出较快的质量评估,单靠人力无法实现,因此自动化测试必不可少
- 项目类型为APP还是WEB,对于web项目…
- 测试人员比较少,则不宜开展较大规模的自动化测试,因为自动化测试并不能节省测试工作时间
|