提示:阅读本章,请先阅读目录
unittest原名为PyUnit,是由java的JUnit衍生而来。unittest是python内置的单元测试框架,具备编写用例、组织用例、执行用例、输出报告等自动化框架的条件。unittest作为官方的测试框架,在测试方面非常基础,可以在此基础上进行二次开发。
python的内置模块
unittest框架 = 测试模块+测试管理模块+测试统计模块
导入方式:
import unittest
四大组件:
下面就讲解四大组件
规则:
import unittest
from selenium import webdriver
class TestCaseLogin(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
"""
初始化,打开浏览器,设置浏览器最大化
:return:
"""
cls.driver = webdriver.Chrome()
cls.driver.maximize_window()
def test_01_open(self):
"""
打开项目
:return:
"""
self.driver.get('http://www.baidu.com/login')
def test_02_input(self):
"""
对账号,密码框,进行赋值
:return:
"""
username = self.driver.find_element_by_id('username')
password = self.driver.find_element_by_id('password')
username.send_keys('admin')
password.send_keys('abc123456')
def test_03_submit(self):
"""
点击登录按钮
:return:
"""
submit = self.driver.find_element_by_id('submit')
submit.click()
这是一个标准的测试用例类,文件名为:
test_case_login.py
一般存放在
testcase 文件夹
路径为:
project/testcase/test_case_login.py
执行测试用例的前置操作,后置操作,相当于把测试用例夹在中间,因此得名,测试夹具
import unittest
from selenium import webdriver
class TestCaseLogin(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
"""
初始化,打开浏览器,设置浏览器最大化
类的第一次执行,会先执行setUpClass,前置操作
:return:
"""
cls.driver = webdriver.Chrome()
cls.driver.maximize_window()
@classmethod
def tearDownClass(cls) -> None:
"""
类的结束之前,会先执行tearDownClass 后置操作
:return:
"""
pass
def setUp(self) -> None:
"""
每个function执行之前,都会执行setUp
:return:
"""
pass
def tearDown(self) -> None:
"""
每个function结束之前,tearDown
:return:
"""
pass
TestSuite:属于比较灵活的,可以添加单个,多个测试用例,主要测试某个类的用例,或者某个类
TestLoader:属于批量加载测试用例库,主要用于某个版本发布的时候,批量运行
# test_case_login.py
import unittest
from selenium import webdriver
class TestCaseLogin(unittest.TestCase):
def setUp(self) -> None:
"""
每个function执行之前,都会执行setUp
:return:
"""
print('方法执行之前,执行了setUp')
def tearDown(self) -> None:
"""
每个function结束之前,tearDown
:return:
"""
print('方法结束之前,执行了tearDown')
def test_01_open(self):
"""
打开项目
:return:
"""
print('执行了open')
def test_02_input(self):
"""
对账号,密码框,进行赋值
:return:
"""
print('执行了input')
def test_03_submit(self):
"""
点击登录按钮
:return:
"""
print('执行了submit')
# run.py
from test_case_login import TestCaseLogin
import unittest
# 实例化一个套件
suite = unittest.TestSuite()
# 往套件,添加一个测试用例
suite.addTest(TestCaseLogin('test_02_input'))
# 套件的run,需要传入一个Result,所以我们实例化一个就可以了
result = unittest.TestResult()
# 运行suite的run,实际上是suite的run会去调用所有测试用例的run
suite.run(result)
运行结果:
方法执行之前,执行了setUp
执行了input
方法结束之前,执行了tearDown
Process finished with exit code 0
from test_case_login import TestCaseLogin
import unittest
suite = unittest.TestSuite()
# suite.addTest(TestCaseLogin('test_02_input'))
suite.addTests([
TestCaseLogin('test_01_open'),
TestCaseLogin('test_03_submit')
])
result = unittest.TestResult()
suite.run(result)
运行结果:
方法执行之前,执行了setUp
执行了open
方法结束之前,执行了tearDown
方法执行之前,执行了setUp
执行了submit
方法结束之前,执行了tearDown
from test_case_login import TestCaseLogin
import unittest
# loadTestsFromTestCase 加载类下面的所有用例
testcase = unittest.TestLoader().loadTestsFromTestCase(
TestCaseLogin
)
# 实例化一个result
result = unittest.TestResult()
# 运行
testcase.run(result)
运行结果:
方法执行之前,执行了setUp
执行了open
方法结束之前,执行了tearDown
方法执行之前,执行了setUp
执行了input
方法结束之前,执行了tearDown
方法执行之前,执行了setUp
执行了submit
方法结束之前,执行了tearDown
我们把目录结构优化一下
并增加了test_case_reg.py
# testcase/test_case_login.py
import unittest
class TestCaseLogin(unittest.TestCase):
def test_01_open(self):
"""
打开项目
:return:
"""
print('执行了open')
def test_02_input(self):
"""
对账号,密码框,进行赋值
:return:
"""
print('执行了input')
def test_03_submit(self):
"""
点击登录按钮
:return:
"""
print('执行了submit')
# testcase/test_case_reg.py
import unittest
class TestCaseReg(unittest.TestCase):
def test_01_reg(self):
"""
打开项目
:return:
"""
print('执行了reg注册')
这样的话,如果我们想运行testcase目录下的所有测试用例
就可以用 unittest.defaultTestLoader.discover
import unittest
# discover 加载路径,start_dir = 路径,pattern = 匹配规则
suite = unittest.defaultTestLoader.discover(
start_dir='testcase',
pattern='test_*.py'
)
result = unittest.TestResult()
suite.run(result)
运行结果:
执行了open
执行了input
执行了submit
执行了reg注册
TestRunner:执行测试用例,并输出结果
import unittest
from testcase.test_case_login import TestCaseLogin
# 实例化套件suite
suite = unittest.TestSuite()
# 添加单个测试用例
suite.addTest(TestCaseLogin('test_01_open'))
# 实例化TextTestRunner运行器,以文本形式展示出来
runner = unittest.TextTestRunner()
# 执行
runner.run(suite)
运行结果:
执行了open
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Process finished with exit code 0
在 Ran 1 test in 0.000s 上面
有 一个点 则代表:用例执行成功
有 E 则代表:用例执行失败
有 F 则代表:用例执行异常(断言失败)
有 S 则代表:用例执行了装饰器skip,跳过不执行
方式一:
可以使用unittest 最基础的测试报告模板
HtmlTestRunner,但是只支持python2,如果要支持python3,就是要修改代码,让里面的语法支持python3
方式二:
可以安装插件 unittestreport
可以生成比较美观的html报告,有3种模板,并且还可以支持多线程
方式三:
可以安装插件 BeatifulReport
有四种主题,跟 unittestreport差不多,但是unittestreport比较强大一点
企业测试报告定制,可以让开发团队,专门开发,独立专业的Html测试报告
方式五:
可以结合pytest+allure,生成更加美观,详细的测试报告,并且allure支持自定义配置,可以给某个企业定制模板
本质上,unittest的规则,适用于pytest的规则,只是有一些地方有差异
比如在测试夹具
unittest,是setUp ,而pytest是 setup
所以要结合pytest的话,这个地方需要注意一下
至于其他的命名规则,文件路径等,几乎一样
目录结构:
# testcase/test_case_login.py
import unittest
class TestCaseLogin(unittest.TestCase):
def test_01_open(self):
"""
打开项目
:return:
"""
print('执行了open')
@unittest.skip
def test_02_input(self):
"""
对账号,密码框,进行赋值
:return:
"""
print('执行了input')
@unittest.skipIf(3 > 2, '符合条件,跳过')
def test_03_submit(self):
"""
点击登录按钮
:return:
"""
print('执行了submit')
# testcase/test_case_reg.py
import unittest
class TestCaseReg(unittest.TestCase):
@unittest.skipUnless(3 > 2, '条件成立,不跳过')
def test_01_reg(self):
"""
打开项目
:return:
"""
print('执行了reg注册')
# run.py
import unittest
suite = unittest.defaultTestLoader.discover(
start_dir='testcase',
pattern='test_*.py'
)
runner = unittest.TextTestRunner()
runner.run(suite)
运行结果:
执行了open
执行了reg注册
..s.
----------------------------------------------------------------------
Ran 4 tests in 0.001s
OK (skipped=1)
POM设计模式,即 page object model 页面对象模型,是web自动化测试应用最广泛的设计模型之一。
其设计思路,就是一个web项目有很多页面,即把一个页面当成一个页面对象来进行设计
python:什么是对象?
通过类class 去描述一组对象,对象=属性+方法
每个页面都会有相同的属性及方法:
比如,点击,输入,元素定位的方法
base/base.py
每个页面会有相同的属性及方法
例如:
点击,输入,定位元素等
page/page_login.py
page/page_reg.py
针对每个页面去定义页面类,页面类会继承基类
比如:
登录页面,输入账号,密码,点击登录
测试用例层,需要继承unittest.TestCase 类
主要包含项目的业务流程
例如:
从登录之后查看购物车,并把购物车清空
测试数据,集中存放,管理测试用例所需要的数据
项目目录结构:
base/base.py
基类,封装所有页面共用的属性及方法
# -*- coding: utf-8 -*-
# @Time : 2022/7/9 10:43
# @Author : ZhongShaoFeng
# @File: base.py
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
class Base:
def __init__(self, url):
"""
初始化,需传入url,打开项目
:param url:
"""
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.get(url)
def base_find_element(self, loc, timeout=30):
"""
查找元素,找到之后返回元素
:param loc: tuple (By.xx, 'xxx')
:param timeout: 超时时间,default=30
:return: element 元素
"""
return WebDriverWait(
driver=self.driver,
timeout=timeout,
poll_frequency=0.5
).until(
lambda x: x.find_element(*loc)
)
def base_click(self, loc):
"""
点击click
:param loc:
:return:
"""
self.base_find_element(loc).click()
def base_input(self, loc, val):
"""
输入send_keys
:param loc:
:param val: 传入输入值
:return:
"""
self.base_find_element(loc).send_keys(val)
def base_text(self, loc):
"""
获取元素的文本内容
:param loc:
:return:
"""
return self.base_find_element(loc).text
page/page_login.py
构建页面对象模型
# -*- coding: utf-8 -*-
# @Time : 2022/7/9 11:01
# @Author : ZhongShaoFeng
# @File: page_login.py
from base.base import Base
from selenium.webdriver.common.by import By
class PageLogin(Base):
# 定义属性,元素定位
username = (By.NAME, 'username')
password = (By.NAME, 'password')
submit = (By.NAME, 'submit')
user_info = (By.NAME, 'userinfo')
def page_login(self):
"""
执行登录操作
:return:
"""
# 输入账号,密码
self.base_input(PageLogin.username, 'admin')
self.base_input(PageLogin.password, 'abc12345678')
# 点击提交
self.base_click(PageLogin.submit)
def page_get_user_info(self):
"""
查看登录之后,用户的信息
:return:
"""
return self.base_text(PageLogin.user_info)
testcase/test_case_login.py
编写测试用例
# -*- coding: utf-8 -*-
# @Time : 2022/7/9 11:07
# @Author : ZhongShaoFeng
# @File: test_case_login.py
import unittest
from page.page_login import PageLogin
class TestCaseLogin(unittest.TestCase):
def setUpClass(self) -> None:
"""
前置处理器,初始化页面
:return:
"""
url = 'http://www.baidu.com/login'
self.login = PageLogin(url=url)
def tearDownClass(self) -> None:
"""
后置处理器,关闭浏览器
:return:
"""
self.login.driver.quit()
def test_01_login(self):
"""
测试:登录功能
:return:
"""
# 登录
self.login.page_login()
# 获取的用户信息
userinfo = self.login.page_get_user_info()
# 断言,查看用户信息标题,是否包含欢迎登录,否则则代表登录失败
self.assertIn('欢迎登录,', userinfo)
testdata
存放一些数据,暂时不用
all.py
运行测试用例
# -*- coding: utf-8 -*-
# @Time : 2022/7/9 10:43
# @Author : ZhongShaoFeng
# @File: all.py
import unittest
suite = unittest.defaultTestLoader.discover(
start_dir='testcase',
pattern='test_*.py'
)
runner = unittest.TextTestRunner()
runner.run(suite)
什么是数据驱动?
数据驱动是现在主流的自动化测试设计模式之一,以数据驱动测试
数据驱动的意义:
通过不同的数据对同一个脚本实现循环测试,最终实现数据与脚本的分离,我们只需要关注数据撰写,无需关注脚本的实现
在企业实战中,可以让项目组长带队,前期构架好自动化测试框架,同时脚本编写好,以数据驱动测试,那么,其他的测试人员,只需要撰写测试用例的数据,然后,通过一键执行脚本,自动生成报告,其他测试人员无需关注脚本是如何实现的,如何编写的。
结合unittest 框架,如何实现数据驱动?
我们可以通过ddt模块实现
ddt安装:pip install ddt
DDT模块:
通过ddt的数据传动,实现循环执行测试用例
@ddt.data(['admin1', 'admin2'])
def test_01_login(self, username):
"""
测试:登录功能
:return:
"""
print(username)
# 登录
self.login.page_login()
# 获取的用户信息
userinfo = self.login.page_get_user_info()
# 断言,查看用户信息标题,是否包含欢迎登录,否则则代表登录失败
self.assertIn('欢迎登录,', userinfo)
比如这里,我们通过ddt.data 传入了一个列表,列表有两组数据,那么,test_01_login 测试用例,就会自动执行两次
什么是关键字驱动?
以关键字函数驱动测试,关键字驱动又叫动作字驱动
就是把业务封装成关键字函数,再基于关键字实现自动化测试
覆盖项目业务=用例集合覆盖测试
用例集合覆盖测试=一个一个的用例实现覆盖测试
实现覆盖测试=多个操作步骤组成
多个操作步骤=多个关键字函数
(那么操作步骤就等于关键字函数)
比如:
登录用例
实际上分为多个步骤组成,打开浏览器,打开登录页面,输入账号,输入密码,再点击登录按钮
那么关键字函数就是:
open_browser() 打开浏览器
load_url() 加载登录页面
input() 输入账号
input() 输入密码
click() 点击登录按钮
这样就实现了操作步骤,也就实现了关键字函数,也就实现了通过关键字函数驱动步骤,从而去驱动测试用例的执行
提示:将会持续更新优化
20220709,TestSuite 测试套件-添加单个测试用例,添加多个测试用例,加载类下的所有用例,加载某个路径下的所有测试用例,TestRunner 测试运行器-TextTestRunner 文本测试报告生成器,生成Html报告,装饰器@unittest.skip强制跳过,@unittest.skipIf 符合条件,则跳过,@unittest.skipUnless 符合条件,则不跳过,POM 设计模型-什么是POM 设计模型?,POM 分为四层设计架构-base 基类,page 页面对象类,testcase 测试用例层,testdata 测试数据,如何基于POM进行自动化测试框架架构?数据驱动和关键字驱动-数据驱动,关键字驱动
20220708,Unittest 框架-TestCase 测试用例,TestFixture 测试夹具