大家好,欢迎来到IT知识分享网。
unittest + HTMLTestRunner
(仅作为个人笔记,如有雷同,请联系删除。。)
nose/nosetests,按照匹配规则收集测试 – [ 先收集,再执行 ]
1、unittest单元测试框架:
(1) 提供用例组织与执行;
(2) 提供丰富的比较方法;
(3) 提供丰富的日志;
# eg:已有一个求和的Count()类
from calculator import Count
import unittest # 导入unittest模块
class TestCount(unittest.TestCase): # 继承unittest的TestCase类。(TestCase类:对特定类进行测试的集合)
def setUp(self): # setUp()方法用于测试用例执行前的初始化工作
print("test start")
def test_add(self): # 测试用例
j = Count(2, 3)
self.assertEqual(j.add(), 5) # assertEqual(),断言,判断两者是否相等
def tearDown(self): # tearDown()方法用于测试用例执行之后的善后工作
print("test end")
if __name__ == '__main__':
unittest.main() # unittest提供了全局的main()方法,可方便地将一个单元测试模块变成可直接运行的测试脚本
# main()方法使用TestLoader类来搜索所有包含在该模块中以"test"命名开头的测试方法,并自动执行它们
2、关于unittest的4个重要概念:
- TestCase:一个TestCase的实例就是一个测试用例。而一个完整的测试用例包括测试前准备环境的搭建(setUp)、实现测试过程的代码(run),以及测试后环境的还原(tearDown)。
- TestSuite:一个功能的验证需要多个测试用例,将多个测试用例集合在一起来执行。TestSuite就是用来组装单个测试用例,可以通过addTest方法加载TestCase到TestSuite中,从而返回一个TestSuite实例。
# 构造测试集:
suite = unittest.TestSuite()`
suite.addTest(类名("方法名"))
- TestRunner:在unittest单元测试框架中,通过TextTestRunner类提供的run()方法来执行TestSuite/TestCase。Testrunner可以使用图形界面、文本界面,或返回一个特殊的值等方式来表示测试结果的执行。
# 运行测试集:
runner = unittest.TextTestRunner()
runner.run(suite)
- TestFixture:对一个测试用例环境的搭建和销毁,就是一个fixture,通过覆盖TestCase的setUp()和tearDown()方法来实现。
# eg:test.py ---->已有一个求和的Count()类,封装在calculator模块中
from calculator import Count
import unittest
class TestCount(unittest.TestCase):
def setUp(self):
print("test start")
def test_add(self):
j = Count(2, 3)
self.assertEqual(j.add(), 5)
def test_add2(self):
j = Count(41, 76)
self.assertEqual(j.add(), 117)
def tearDown(self):
print("test end")
if __name__ == '__main__':
suite = unittest.TestSuite() # 创建测试集
suite.addTest(TestCount("test_add2")) # 将第二个测试用例加载到suite测试集中
runner = unittest.TextTestRunner()
runner.run(suite) # 运行suite测试集中所组装的测试用例
3、unittest.TestCase类提供的断言方法:
assertEqual(first,second,msg=None)
:断言第一个参数和第二个参数相等,若不相等则测试失败。msg为可选参数,定意测试失败时打印的信息assertNotEqual(first,second,msg=None)
:断言第一个参数和第二个参数不相等,若相等则测试失败assertTrue(表达式,msg=None)
:断言表达式结果为TrueassertFalse(表达式,msg=None)
:断言表达式结果为FalseassertIn(first,second,msg=None)
:断言第一个参数在第二个参数中assertNotIn(first,second,msg=None)
:断言第一个参数不在第二个参数中assertIs(first,second,msg=None)
:断言第一个参数和第二个参数是同一个对象assertIsNot(first,second,msg=None)
:断言第一个参数和第二个参数不是同一个对象assertIsNone(表达式,msg=None)
:断言表达式为None对象assertIsNotNone(表达式,msg=None)
:断言表达式不为None对象assertIsInstance(obj,cls,msg=None)
:断言obj是cls的一个实例assertNotIsInstance(obj,cls,msg=None)
:断言obj不是cls的实例
# eg:unittest.TestCase类的断言方法.py
import unittest
class Test(unittest.TestCase):
def setUp(self):
print("test start")
def test_case(self):
a = "hello"
b = "hello world"
self.assertIn(a, b, msg="a不在b中!")
def tearDown(self):
print("test end")
if __name__ == "__main__":
unittest.main()
4、discover()方法: 由TestLoader类提供,使unittest单元测试框架自动识别测试用例
discover(start_dir, pattern='test*.py', top_level_dir=None)
:找到指定目录下所有测试模块,并可递归查到子目录下的测试模块,只有匹配到测试用例文件名才能被加载到测试集。如果启动的不是顶层目录,那么顶层目录必须单独指定。
(1)start_dir
:要测试的模块名或测试用例目录
(2)pattern='test*.py'
:表示用例文件名的匹配规则。此处匹配文件名以”test”开头的”.py”类型的文件,”*”表示任意多个字符
(3)top_level_dir=None
:测试模块的顶层目录,如果没有顶层目录,默认为None
# eg:unittest.TestCase类的断言方法.py
import unittest
class Test(unittest.TestCase):
def setUp(self):
print("test start")
def test_case(self):
a = "hello"
b = "hello world"
self.assertIn(a, b, msg="a不在b中!")
def tearDown(self):
print("test end")
if __name__ == "__main__":
unittest.main()
5、unittest执行测试用例的顺序:
默认根据ASCII码的顺序加载测试用例,数字与字母的顺序为:0-9,A-Z,a-z。对于测试目录与测试文件也是一样的加载顺序,只能通过测试用例的命名来提高被执行的优先级。
6、执行多级目录的用例:
假设有测试目录,对于这样的目录结构,若将discover()方法中的start\_dir参数定义为“./test\_case/”目录,只能加载test\_a.py文件中的测试用例。如果要让unittest框架找到test\_case/的子目录中的测试文件,需要在每个子目录下放一个\_\_init\_\_.py文件。
test_project/
test_case/
test_bbb/
test_ccc/
test_c.py
test_b.py
test_ddd/
test_d.py
test_a.py
7、跳过测试和预期失败:
unittest提供了如下装饰器:
unittest.skip(reason)
:无条件的跳过装饰的测试,说明跳过测试的原因unittest.skipIf(condition, reason)
:如果条件为真,跳过装饰的测试unittest.skipUnless(condition, reason)
:除非条件为真,否则跳过装饰的测试unittest.expectedFailure()
:测试标记为失败。不管执行结果是否失败,统一标记为失败
# eg:test.py
import unittest
class MyTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
@unittest.skip("直接跳过测试")
def test_skip(self):
print("test_aaa")
@unittest.skipIf(3>2, "当条件为True时跳过测试")
def test_skip_if(self):
print("test_bbb")
@unittest.skipUnless(3>2, "除非条件为真,否则跳过测试")
def test_skip_unless(self):
print("test_ccc")
@unittest.expectedFailure
def test_expected_failure(self):
assertEqual(2, 3)
if __name__ == '__main__':
unittest.main()
# 注:这些装饰器同样可以作用于测试类,只需将其定义在测试类上面即可。
import unittest
@unittest.skip("直接跳过测试该测试类")
class Mytest(unittest.TestCase):
......
8、fixtures:
就是使用setUp和tearDown来前后包含测试用例。此外,unittest还提供了对于测试类和模块的fixtures。
setUpModule、tearDownModule
:在整个模块的开始与结束时被执行。setUpClass、tearDownClass
:在测试类的开始与结束时被执行,但需要通过@classmethod进行装饰,并且其方法的参数为clssetUp、tearDown
:在测试用例的开始与结束时被执行
# eg:测试用例、测试类和模块的fixtures.py
import unittest
def setUpModule():
print("测试模块开始执行 >>>>>>>>>>>>>")
def tearDownModule():
print("测试模块执行结束 >>>>>>>>>>>>>")
class Test(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("测试类开始执行 ==========>")
@classmethod
def tearDownClass(cls):
print("测试类执行结束 ==========>")
def setUp(self):
print("测试用例开始执行 --------------->")
def tearDown(self):
print("测试用例执行结束 --------------->")
def test_case1(self):
print("测试用例1")
def test_case2(self):
print("测试用例2")
if __name__ == '__main__':
unittest.main()
9、保存测试结果:
写好测试用例文件xxx.py、yyy.py以及测试用例执行文件runtest.py后,首先打开Windows命令提示符,切换到runtest.py所在目录,执行命令:python runtest.py >> report/log.txt 2>&1
,(report是与runtest.py同级的目录,其目录下方含有log.txt文件),然后系统自动执行runtest.py文件,会将测试结果保存在report目录下的log.txt文件中,打开…/report/log.txt文件,可查看测试结果。
10、HTMLTestRunner:
unittest单元测试框架的一个扩展,生成易于使用的HTML测试报告。
- 下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html,打开网址下载HTMLTestRunner.py文件,下载之后将其放到D:\Python\Lib目录下即可。(注:下载到的是Python2的,需要手动修改为Python3版的,或者直接在网上下载Python3版的即可)
11、使用HTMLTestRunner生成HTTL测试报告:
要注意HTMLTestRunner()和unittest.TextTestRunner()的区别
from HTMLTestRunner import HTMLTestRunner # 导入HTMLTestRunner
......
f = open('./result.html', 'wb') # 以二进制写的形式打开当前目录下的result.html,如果没有,则自动创建该文件
runner = HTMLTestRunner(stream=f, # stream指定测试报告文件
title='测试报告标题', # title用于定义测试报告的标题
description='测试报告的副标题') # description用于定义测试报告的副标题
runner.run(测试集) # 通过HTMLTestRunner的run()方法运行测试集中所组装的测试用例
f.close() # 关闭测试报告文件
# eg:生成HTML测试报告.py
from selenium import webdriver
import unittest
from HTMLTestRunner import HTMLTestRunner
class Baidu_test(unittest.TestCase):
def setUp(self):
self.driver=webdriver.Firefox()
self.driver.implicitly_wait(10)
self.base_url="http://www.baidu.com/"
def test_baidu_search(self):
driver = self.driver
driver.get(self.base_url)
driver.find_element_by_id("kw").send_keys("HTMLTestRunner")
driver.find_element_by_id("su").click()
def tearDown(self):
self.driver.quit()
if __name__ == "__main__":
suite = unittest.TestSuite() #创建测试集
suite.addTest(Baidu_test("test_baidu_search")) #向测试集中组装测试用例
f = open('./result.html', 'wb') #定义测试报告文件存放路径,创建并打开测试报告文件
runner = HTMLTestRunner(stream=f, title='百度搜索测试报告', description='用例执行情况:') #定义测试报告
runner.run(suite) #运行测试集中的测试用例
f.close() #关闭报告文件
12、生成更易读的测试报告:
为自动化测试用例加上标题——在类或方法的下方,通过三引号(“”” “””或’’’ ‘’’)来添加docstring类型的注释,此注释在平时调用的时候不显示。而HTMLTestRunner可以读取这类注释,显示在测试报告中。
# eg:借用上例:
.......
class Baidu_test(unittest.TestCase):
'''百度搜索测试'''
.......
def test_baidu_search(self):
'''搜索关键字:HTMLTestRunner'''
......
13、测试报告文件名:
使每次生成的测试报告名称都不重复且有意义,最好的办法是在报告名称中加入当前时间,这样生成的报告既不会重叠,也能更清晰的知道报告的生成时间。
import time
:导入time模块
time.time()
:获取当前时间戳time.ctime()
:当前时间的字符串形式time.localtime()
:当前时间的struct_time形式time.strftime
:用来获得当前时间,可将时间格式化为字符串。
# eg:继续打开上面的百度搜索测试用例,作如下修改即可:
import time
......
if __name__ == "__main__":
......
now = time.strftime("%Y-%m-%d %H_%M_%S")
filename = './' + now + 'result.html'
f = open(filename, 'wb')
.......
14、项目集成测试报告:使HTMLTestRunner作用于整个测试项目
# eg:runtest.py -------->测试用例执行文件
import unittest,time
from HTMLTestRunner import HTMLTestRunner
test_dir = './test_case'
discover = unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py')
if __name__ == '__main__':
now = time.strftime("%Y-%m-%d %H_%M_%S")
filename = test_dir + '/' + now + 'result.html'
f = open(filename, 'wb')
runner = HTMLTestRunner(stream=f, title='测试报告', description='用例执行情况:')
runner.run(discover)
f.close()
15、自动发邮件功能:
需要开启POP3/SMTP,并开启客户端授权密码:登录163邮箱–设置–客户端授权密码–开启
# 具体参考:Python笔记,49. 发送邮件
import smtplib # 导入smtplib模块
connect(host,port):# host是指定连接的邮箱服务器,port指定连接服务器的端口号
login(user, password):# user是登录邮箱用户名,password是登录邮箱的密码
sendmail(from_addr,to_addrs,msg):# from_addr是邮件发送者地址,to_addrs是字符串列表,邮件接收者地址,msg是发送的邮件
quit():# 结束SMTP会话
# eg:发送HTML格式的邮件.py
import smtplib
from email.mime.text import MIMEText
from email.header import Header
smtpserver = 'smtp.126.com' # 邮箱服务器
user = 'daqiang@126.com' # 发送邮箱的用户名和密码
password = '123456'
sender = 'lixiaojiangde126@126.com' # 发送方邮箱
receiver = '@qq.com' # 接收方邮箱
subject = 'Python发送邮件练习'
msg = MIMEText('<html><h1>你好!</h1></html>', 'utf-8') # 编写HTML类型的邮件正文
msg['Subject'] = Header(subject, 'utf-8') # 添加邮件主题
smtp = smtplib.SMTP() # 创建SMTP对象
smtp.connect(smtpserver) # 连接邮箱服务器
smtp.login(user, password) # 登录邮箱
smtp.sendmail(sender, receiver, msg.as_string()) # 发送邮件
smtp.quit() # 退出
16、查找最新的测试报告:
测试报告的名称是根据当前时间生成的
# eg:查找最新的测试报告.py
import os
result_dir = 'C:\\Users\Administrator\\Desktop\\Selenium 2练习' # 定义测试报告的目录
lists = os.listdir(result_dir) # 获取目录下的所有文件及文件夹
lists.sort(key=lambda fn: os.path.getmtime(result_dir+'\\'+fn)) # 对目录下的文件及文件夹按时间重新排序
print(('最新的文件为:' + lists[-1]))
file = os.path.join(result_dir, lists[-1]) # 合成文件的路径
print(file)
17、将自动发邮件功能整合到自动化测试项目中:
整个程序的执行步骤可以分为三部分,如下:
- 通过unittest框架的discover()找到匹配的测试用例,由HTMLTestRunner的run()方法执行测试用例并生成最新的测试报告文件;
- 调用new_report()函数找到测试报告目录下最新生成的测试报告,返回测试报告的路径;
- 将得到的最新测试报告的完整路径传给send_mail()函数,实现发送邮件功能。
# eg:runtest.py -------->测试用例执行文件
import unittest,time,os
from HTMLTestRunner import HTMLTestRunner
from email.mime.text import MIMEText
from email.header import Header
import smtplib
def send_mail(file_new): # 定义发送邮件函数
f = open(file_new, 'rb'')
mail_body = f.read()
f.close()
msg = MIMEText(mail_body, 'html', 'utf-8')
msg['Subject'] = Header("自动化测试报告" ,'utf-8')
smtp = smtplib.SMTP()
smtp.connect("smtp.126.com")
smtp.login("daqiang@126.com", "password")
smtp.sendmail("daqiang@126.com", "14xxxxxx28@qq.com", msg.as_string())
smtp.quit()
print('邮件发送完毕!')
def new_report(testreport): # 查找测试报告目录,找到最新生成的测试报告文件
lists = os.listdir(testreport)
lists.sort(key=lambda fn: os.path.getmtime(testreport + "\\" + fn))
file_new = os.path.join(testreport, lists[-1])
print(file-new)
return file_new
if __name__ == '__main__':
test_dir = 'D:\\testpro\\test_case'
test_report = 'D:\\testpro\\report'
discover = unnittest.defautTestLoader.discover(test_dir, pattern='test_*.py')
now = time.strftime("%Y-%m-%d %H_%M_%S")
filename = test_report + '\\' + now + 'result.html'
fp = open(filename, 'wb')
runner = HTMLTestRunner(stream=fp, title="自动化测试报告", description="用例执行情况:")
runner.run(discover)
fp.close()
file_new_report = new_report(test_report) # 找到最新生成的测试报告文件
send_mail(file_new_report) # 发送测试报告
18、Page Object设计模式:
page对象应当将在GUI控件上的所有查询和操作数据的行为封装为方法。即使改变具体的控件,page对象的接口也不应当发生变化。虽然术语是“页面”对象,但并不意味着需要针对每个页面建立一个这样的对象,例如:页面有重要意义的元素可以独立为一个page对象。
· Page Object设计模式 = 页面对象层 + 测试用例
以登录126邮箱为例,通过Page Object设计模式来实现:
- 创建Page类:首先创建基础类Page,在初始化方法__init__()中定义驱动(driver)、基本的URL(base_url)和超时时间(timeout)等;
(1)定义open()方法用于打开URL网站;
(2)关于URL地址的断言部分,则交由on_page()方法来实现;
(3)find_element()方法用于元素的定位。 - 创建LoginPage类:Page类中定义的方法都是页面操作的基本方法,在此处根据登录页面的特点再创建LoginPage类并继承Page类,这也是Page Object设计模式中最重要的对象层。
LoginPage类中主要对登录页面上的元素进行封装,使其成为更具体的操作方法。例如:用户名、密码、登录按钮都被封装成了方法。
3.创建test_user_login()函数:将单个的元素操作组成一个完整的动作,而这个动作包含了打开浏览器、输入用户名/密码、点击登录按钮等单步操作。在使用该函数时需要将driver、username、password等信息作为函数的入参,这样该函数具有很强的可用性。
4. 创建main()函数:main()函数更接近于用户的操作行为。对于用户来说,要进行邮箱的登录,需要关心的是通过那个浏览器打开邮箱的网址、登录的用户名和密码是什么,至于输入框、按钮是如何定位的,则不需要关心。
注1:这样分层的好处就是,不同的层关心不同的问题。页面对象层只关心元素的定位问题,测试用例只关心测试的数据。
注2:关于page对象是否应该自身包含断言,或者仅仅提供数据给测试脚本来设置断言——这是一个有分歧的地方,建议在page对象中不包含断言。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/12014.html