UI自动化——单元测试框架(1)
1、自动化测试概述
1.1自动化测试维度
(1)表单的错误提示信息的验证,如弹出框文本信息的验证,邮箱等输入的格式不规范。
(2)产品业务逻辑的验证;
(3)页面的各种交互。
1.2自动化测试步骤
1、先梳理哪些模块可以做自动化测试。
2、梳理完成后,和相关的人建议对下计划以及梳理的结果。
3、编写代码实现梳理的测试模块。
4、编写完成后,和相关的人进行评审代码(测试场景是否考虑周全,测试断言是否合理,代码编写的是否合理)。
5、编写完成后,整合到Jekins的持续集成平台。
6、下个迭代的时候,直接可以应用于产品的回归测试中。
2、自动化测试用例组成
1、初始化;
2、测试步骤;
3、断言;
4、清理。
如以百度搜索“赵丽颖”为例:
1、初始化就是打开浏览器,进入百度;
2、测试步骤:就是我们输入关键字“赵丽颖”然后搜索到相关内容的过程;
3、断言:就是断言我们搜索到的内容与输入的关键字的比较("is"、"in"、"==")
4、清理:就是你关闭浏览器的过程。
2.1断言的方式
2.1.1python原生的assert断言
import unittest
from selenium import webdriver
class BaiduTest(unittest.TestCase):
#初始化
def setUp(self) -> None:
self.driver=webdriver.Chrome()
self.driver.maximize_window()
self.driver.get("http://www.baidu.com")
self.driver.implicitly_wait(20)
'''验证百度的标题''' #测试用例注释
# 步骤和断言
def test_baidu_title(self):
#断言内容
'''验证百度的标题'''
assert self.driver.title == "百度一下,你就知道"
#清理
def tearDown(self) -> None:
self.driver.quit()
if __name__ == '__main__':
#main():执行测试类中的所有测试用例。
unittest.main()
2.1.2selenium原生的断言
(1)assertEqual:比较两个对象是否相等
import unittest
from selenium import webdriver
import time
#测试固件使用常规测试固件:
class SinaTest(unittest.TestCase):
#初始化
def setUp(self) -> None:
self.driver=webdriver.Chrome()
self.driver.maximize_window()
self.driver.get("https://mail.sina.com.cn/")
self.driver.implicitly_wait(20)
#测试步骤和断言
def test_sina_login_formatError(self):
'''验证登录输入框格式不正确的提示信息''' #测试用例注释
self.driver.find_element_by_id("freename").send_keys("asdwer")
self.driver.find_element_by_id("freepassword").send_keys("123456")
self.driver.find_element_by_class_name("loginBtn").click()
time.sleep(3)
#断言内容
information=self.driver.find_element_by_xpath("/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]")
'''assertEqual:比较两个对象是否相等,等同于字符串的比较“==”
self.assertEqual(information.text,"您输入的邮箱名格式不正确")
# 清理
def tearDown(self) -> None:
self.driver.quit()
if __name__ == '__main__':
#main():执行测试类中的所有测试用例。
unittest.main()
(2)assertIN:比较一个对象是否包含另一个对象;
import unittest
from selenium import webdriver
import time
#测试固件使用常规测试固件:
class SinaTest(unittest.TestCase):
#初始化
def setUp(self) -> None:
self.driver=webdriver.Chrome()
self.driver.maximize_window()
self.driver.get("https://mail.sina.com.cn/")
self.driver.implicitly_wait(20)
#测试步骤和断言
def test_sina_login_formatError(self):
'''验证登录输入框格式不正确的提示信息''' #测试用例注释
self.driver.find_element_by_id("freename").send_keys("asdwer")
self.driver.find_element_by_id("freepassword").send_keys("123456")
self.driver.find_element_by_class_name("loginBtn").click()
time.sleep(3)
#断言内容
information=self.driver.find_element_by_xpath("/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]")
'''assertIn:比较一个对象是否包含另一个对象,等同于字符串的比较“in”'''
self.assertIn(information.text,"您输入的邮箱名格式不正确")
# 清理
def tearDown(self) -> None:
self.driver.quit()
if __name__ == '__main__':
#main():执行测试类中的所有测试用例。
unittest.main()
(3)assertTrue:针对bool类型验证true or false;真真为真,真假为假,即断言内容返回真,则为真,断言内容返回假则为假。
import unittest
from selenium import webdriver
class SinaTest(unittest.TestCase):
#初始化
def setUp(self) -> None:
self.driver=webdriver.Chrome()
self.driver.maximize_window()
self.driver.get("https://mail.sina.com.cn/")
self.driver.implicitly_wait(20)
def test_autologin(self):
'''验证自动登录受否勾选''' #测试用例注释
#断言部分
information = self.driver.find_element_by_id("store1")
'''assertTrue:针对bool类型验证,is_selected():对象是否被勾选'''
self.assertTrue(information.is_selected())
# 清理
def tearDown(self) -> None:
self.driver.quit()
if __name__ == '__main__':
#main():执行测试类中的所有测试用例。
unittest.main()
(4)assertFalse:针对bool类型验证true or false;假假为真,假真为假,即断言内容返回假,则为真,断言内容返回真则为假。
import unittest
from selenium import webdriver
class SinaTest(unittest.TestCase):
#初始化
def setUp(self) -> None:
self.driver=webdriver.Chrome()
self.driver.maximize_window()
self.driver.get("https://mail.sina.com.cn/")
self.driver.implicitly_wait(20)
#测试步骤和断言
def test_autologin(self):
'''验证自动登录受否勾选''' #测试用例注释
#断言部分
information = self.driver.find_element_by_id("store1")
'''assertFalse:针对bool类型验证,is_selected():对象是否被勾选,'''
self.assertFalse(information.is_selected())
# 清理
def tearDown(self) -> None:
self.driver.quit()
if __name__ == '__main__':
#main():执行测试类中的所有测试用例。
unittest.main()
3、单元测试
在python中,单元测试的框架主要就是unittest。unittest属于标准库,只要安装了python解释器后就可以直接导入使用了。导入的方式为:import unittest。自动化测试的方法是由unittest库下的TestCase类提供的,所以我们写自动化测试用例前必须继承unittest库中的TestCase类。
4、unittest测试框架
注意事项:
1、在测试类里面编写的测试方法必须是test开头的,也就是测试的步骤和断言部分的方法名,必须以test开头,推荐使用test_,并且一言以蔽之,如测试百度的标题名的测试用例,其方法名为:test_baidu_title(self)。
2、在测试类中,若使用的是常规的测试固件,那么有多少个测试用例,其里面的初始化和清理就执行多少次。
3、如果在测试类中,有很多个测试用例,而我们仅仅想执行某一个测试用例,那么就将鼠标放在该测试用例的名称处,然后运行即可。
4.1unittest组件
unittest是属于Python语言的单元测试框架,它的核心组件如下:
4.1.3测试用例
在自动化测试类中编写的测试用例不需要刻意的进行排序,让测试框架内部按照他的排序规则去执行,其内通过是通过ascill码的规则将测试用例方法名称的字母转化为数字,这里不包含test这部分,是指test_后面的用例名称按照从小到大的顺序执行。
注意事项:
1、每个测试用例都需要有名字,也就是每个方法都必须由注释信息。
2、编写的每一个自动化测试用例都必须是独立的,和其他的测试用例之间没有任何的依赖性。
3、每个测试用例都必须由断言。
4、测试方法也就是测试用例的名称必须以test开头,最好规范、有约束、一言以蔽之,如test_。
5、最好一个测试用例方法对应一个业务测试点,不要多个业务检查点写在一个测试用例中。
6、如果涉及到业务逻辑的处理,最好把业务逻辑的处理放在断言前面,避免因为业务逻辑执行错误导致断言失败。
4.1.2测试固件
测试固件表示一个测试用例或者多个测试以及清理工作所需要的设置或者准备,也就是初始化和清理的步骤。
4.1.2.1常规测试固件
常规的测试固件:初始化setUp(self),清理tearDown(self)
import unittest
from selenium import webdriver
class BaiduTest(unittest.TestCase):
#使用常规测试固件:使用常规测试固件:也就是一个测试类中,每执行一个测试用例,测试固件就执行一次
#初始化
def setUp(self) -> None:
self.driver=webdriver.Chrome()
self.driver.maximize_window()
self.driver.get("http://www.baidu.com")
self.driver.implicitly_wait(20)
'''验证百度的标题''' #测试用例注释
# 步骤和断言
def test_baidu_title(self):
'''验证百度的标题'''
assert self.driver.title == "百度一下,你就知道"
#清理
def tearDown(self) -> None:
self.driver.quit()
if __name__ == '__main__':
#main():执行测试类中的所有测试用例。
unittest.main()
4.1.2.2类测试固件
类测试固件:指的是不管一个测试类里面有多少个测试用例,测试固件只执行一次。初始化setUp(cls),清理tearDown(cls),需要在@classmethod的装饰下使用,也就是将测试固件使用类方法,它的缺点是执行完一个测试用例后,需要回归到页面的初始化,也就要进行页面的刷新。
import unittest
from selenium import webdriver
class BaiduTest(unittest.TestCase):
# 使用类测试固件:也就是一个测试类中,无论有多少个测试用例,测试固件只执行一次
@classmethod
#初始化
def setUp(cls) -> None:
cls.driver=webdriver.Chrome()
cls.driver.maximize_window()
cls.driver.get("http://www.baidu.com")
cls.driver.implicitly_wait(20)
'''验证百度的标题''' #测试用例注释
# 步骤和断言
def test_baidu_title(self):
'''验证百度的标题'''
assert self.driver.title == "百度一下,你就知道"
#清理
def tearDown(self) -> None:
self.driver.quit()
if __name__ == '__main__':
#main():执行测试类中的所有测试用例。
unittest.main()
4.1.2.3测试固件的分离
我们可以通过继承的思想,将测试固件分离出来,以确保当地址和浏览器改变时,我们操作比较简单。分离测试固件的时候,我们需要继承unittest.TestCase类,这是因为我们要调用的setUp(self)和tearDown(self)的方法是TestCase类中的,分离出来后,我们在测试类中继承我们分离出来的固件类就可以了。
import time
import unittest
from selenium import webdriver
class Init(unittest.TestCase):
def setUp(self) -> None:
self.driver=webdriver.Chrome()
self.driver.maximize_window()
self.driver.get("https://mail.sina.com.cn/")
time.sleep(3)
self.driver.implicitly_wait(30)
def tearDown(self) -> None:
self.driver.quit()
在后面的测试类中直接继承固件类即可
import unittest
from selenium import webdriver
from 基础学习文件.unittest实战.init import Init
import time
# #测试固件使用常规测试固件:
class SinaTest(Init): #继承固件类
#测试步骤和断言
def test_sina_login_empty(self):
'''验证登录输入框为空的提示信息'''
self.driver.find_element_by_id("freename").send_keys("")
self.driver.find_element_by_id("freepassword").send_keys("")
self.driver.find_element_by_class_name("loginBtn").click()
time.sleep(3)
information=self.driver.find_element_by_xpath("/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]")
'''assertEqual:比较两个对象是否相等,等同于字符串的比较”==“'''
self.assertEqual(information.text,"请输入邮箱名")
time.sleep(5)
def test_sina_login_formatError(self):
'''验证登录输入框格式不正确的提示信息'''
self.driver.find_element_by_id("freename").send_keys("asdwer")
self.driver.find_element_by_id("freepassword").send_keys("123456")
self.driver.find_element_by_class_name("loginBtn").click()
time.sleep(3)
information=self.driver.find_element_by_xpath("/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]")
'''assertIn:比较一个对象是否包含另一个对象'''
self.assertIn(information.text,"您输入的邮箱名格式不正确")
time.sleep(5)
4.1.5测试套件
测试套件顾名思义就是相关测试用例的集合。在unittest中主要通过TestSuite类提供对测试套件的支持,执行测试套件用的方法是unittest库下的TextTestRunner()类中的run的方法。
1、按测试类执行的方式:我们用的是unittest库下的TestLoader()类中的loadTestsFromTestCase的方法。
import os
import unittest
#测试套件:
#按测试类来执行:
suite=unittest.TestLoader().loadTestsFromTestCase(SinaTest)
unittest.TextTestRunner().run(suite)
2、按模块来执行的方式:我们用的是unittest库下的TestLoader()类中的discover的方法。
import os
import unittest
#按模块来执行:
if __name__=="__main__":
#start_dir=加载所有的测试模块来执行,pattern通过正则的方式获取到要执行的模块。
suite = unittest.TestLoader().discover(start_dir=os.path.dirname(__file__),pattern="test_sina.py")
unittest.TextTestRunner().run(suite)
第二种方式:
import unittest
from selenium import webdriver
from 基础学习文件.unittest实战.init import Init
class Baidu(Init):
def test_baidu_title(self):
'''验证百度首页的title信息'''
assert self.driver.title=='百度一下,你就知道'
class BaiDuSo(Init):
def test_baidu_so_value(self):
'''百度搜索关键字的验证'''
so=self.driver.find_element_by_id('kw')
so.send_keys('Selenium4')
assert so.get_attribute('value')=='Selenium4'
if __name__ == '__main__':
'''按测试模块执行,用的是TestLoader类中的loadTestsFromModule方法。'''
suite=unittest.TestLoader().loadTestsFromModule('test_module.py')
unittest.TextTestRunner(verbosity=2).run(suite)
3、加载所有模块来执行的方式:用的是unittest库下的TestLoader()类中的discover的方法。
import os
import unittest
#加载所有的测试模块来执行
#第一种方式:
if __name__=="__main__":
#start_dir=加载所有的测试模块来执行,pattern通过正则的方式获取到
suite = unittest.TestLoader().discover(start_dir=os.path.dirname(__file__),pattern="test_*.py")
unittest.TextTestRunner().run(suite)
#第二种方式:
def getSuite():
suite = unittest.TestLoader().discover(start_dir=os.path.dirname(__file__), pattern="test_*.py")
return suite
if __name__ == '__main__':
unittest.TextTestRunner().run(getSuite())
4.1.4测试执行
TestRunner:测试执行指的是针对测试套件或者是测试用例进行执行的过程。
4.1.5测试报告
TestReport:所有的测试测试用例执行完成后输出的汇总结果报告信息。unittest生成测试报告需要使用到第三方的HTMLTestRunner的库,下载该库后,把该库放在Python安装目录下的lib目录下。导入的方法为:import HTMLTestRunner。获取测试报告的步骤:
(1)在当前测试路径下创建一个report文件夹;
(2)加载所有的测试模块;
(3)获取测试报告。
import HTMLTestRunner
import os
import unittest
import time
#生成自动化测试报告:
#加载所有的测试模块:unittest库下的TestLoader()类中的discover的方法。
def getSuite():
suite = unittest.TestLoader().discover(start_dir=os.path.dirname(__file__), pattern="test_*.py")
return suite
#获取当前时间,由于python格式的限制,当前时间最好采用下面的方式:
def getNowTime():
return time.strftime("%y-%m-%d %H_%M_%S",time.localtime(time.time()))
#返回当前测试路径
def base_dir():
return os.path.dirname(__file__)
#获取测试报告
def Report():
#定义文件名,定义文件名为当前时间+report.html,主要是为为了区分不同时间段生成的测试报告。
filename=os.path.join(base_dir(),"report",getNowTime()+"report.html")
#把测试报告写入文件中,b是以二进制的方式写入
fp=open(filename,"wb")
runner=HTMLTestRunner.HTMLTestRunner(stream=fp,title="UI自动化测试",description="UI自动化测试测试报告")
runner.run(getSuite())
if __name__ == '__main__':
Report()
然后就可以在report文件夹中看到当前时间生成的测试报告,根据下图方式打开:
就可以看到测试报告,如下:
5、单元测试框架之参数化
5.1参数化概述
参数化就是指相同的测试步骤,不同的测试数据,那么这样的测试场景我们就可以使用参数化的解决思路来解决。也就是说使用一个测试用例的代码,执行多个测试场景。参数化需要借助外部的parameterized的库,安装命令:pip3 install parameterized。
5.2参数化本质
针对测试数据进行循环,每次循环的时候对列表中的元素的值一一赋值的过程。
5.3参数化示例
这里以新浪邮箱的登录输入框(用户名、密码)为例:
import unittest
from parameterized import parameterized,param
from selenium import webdriver
import time
#单元测试框架之参数化
class SinaTest(unittest.TestCase):
#初始化
def setUp(self) -> None:
self.driver=webdriver.Chrome()
self.driver.maximize_window()
self.driver.get("https://mail.sina.com.cn/")
self.driver.implicitly_wait(20)
#参数化需要借助外部的parameterized的库中的expand方法,param指向的是每次循环的测试数据。分别指用户名、密码和点击登录后提示的文本信息
@parameterized.expand([param("","","请输入邮箱名"),param("asdwer","123456","您输入的邮箱名格式不正确"),param("asd123@sina.com","123456","登录名或密码错误")])
def test_sina_login(self,username,password,Assert):
'''验证登录输入框输入不同内容的场景'''
self.driver.find_element_by_id("freename").send_keys(username)
self.driver.find_element_by_id("freepassword").send_keys(password)
self.driver.find_element_by_class_name("loginBtn").click()
time.sleep(3)
#断言部分
information=self.driver.find_element_by_xpath("/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]")
'''assertEqual:比较两个对象是否相等,等同于字符串的比较”==“'''
self.assertEqual(information.text,Assert)
# 清理
def tearDown(self) -> None:
self.driver.quit()
if __name__ == '__main__':
unittest.main()
测试报告如下图: