unittest + HTMLTestRunner

unittest + HTMLTestRunnerunittest+HTMLTestRunner1、unittest单元测试框架:(1)提供用例组织与执行;(2)提供丰富的比较方法;(3)提供丰富的日志;#eg:已有一个求和的Count()类fromcalculatorimportCountimportunittest#导入unittest模块classTestCount(unittest.TestCase):#继承unittest的TestCase类。(TestCase类:对特定类进行测试的集合)

大家好,欢迎来到IT知识分享网。unittest

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个重要概念:

  1. TestCase:一个TestCase的实例就是一个测试用例。而一个完整的测试用例包括测试前准备环境的搭建(setUp)、实现测试过程的代码(run),以及测试后环境的还原(tearDown)。
  2. TestSuite:一个功能的验证需要多个测试用例,将多个测试用例集合在一起来执行。TestSuite就是用来组装单个测试用例,可以通过addTest方法加载TestCase到TestSuite中,从而返回一个TestSuite实例。
		# 构造测试集:
		suite = unittest.TestSuite()`
		suite.addTest(类名("方法名"))
  1. TestRunner:在unittest单元测试框架中,通过TextTestRunner类提供的run()方法来执行TestSuite/TestCase。Testrunner可以使用图形界面、文本界面,或返回一个特殊的值等方式来表示测试结果的执行。
    # 运行测试集:
    runner = unittest.TextTestRunner()
	runner.run(suite)
  1. 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类提供的断言方法:

  1. assertEqual(first,second,msg=None):断言第一个参数和第二个参数相等,若不相等则测试失败。msg为可选参数,定意测试失败时打印的信息
  2. assertNotEqual(first,second,msg=None):断言第一个参数和第二个参数不相等,若相等则测试失败
  3. assertTrue(表达式,msg=None):断言表达式结果为True
  4. assertFalse(表达式,msg=None):断言表达式结果为False
  5. assertIn(first,second,msg=None):断言第一个参数在第二个参数中
  6. assertNotIn(first,second,msg=None):断言第一个参数不在第二个参数中
  7. assertIs(first,second,msg=None):断言第一个参数和第二个参数是同一个对象
  8. assertIsNot(first,second,msg=None):断言第一个参数和第二个参数不是同一个对象
  9. assertIsNone(表达式,msg=None):断言表达式为None对象
  10. assertIsNotNone(表达式,msg=None):断言表达式不为None对象
  11. assertIsInstance(obj,cls,msg=None):断言obj是cls的一个实例
  12. 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单元测试框架自动识别测试用例

  1. 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提供了如下装饰器:

  1. unittest.skip(reason):无条件的跳过装饰的测试,说明跳过测试的原因
  2. unittest.skipIf(condition, reason):如果条件为真,跳过装饰的测试
  3. unittest.skipUnless(condition, reason):除非条件为真,否则跳过装饰的测试
  4. 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。

  1. setUpModule、tearDownModule:在整个模块的开始与结束时被执行。
  2. setUpClass、tearDownClass:在测试类的开始与结束时被执行,但需要通过@classmethod进行装饰,并且其方法的参数为cls
  3. setUp、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测试报告

  1. 下载地址: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模块

  1. time.time():获取当前时间戳
  2. time.ctime():当前时间的字符串形式
  3. time.localtime():当前时间的struct_time形式
  4. 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、将自动发邮件功能整合到自动化测试项目中:

整个程序的执行步骤可以分为三部分,如下:

  1. 通过unittest框架的discover()找到匹配的测试用例,由HTMLTestRunner的run()方法执行测试用例并生成最新的测试报告文件;
  2. 调用new_report()函数找到测试报告目录下最新生成的测试报告,返回测试报告的路径;
  3. 将得到的最新测试报告的完整路径传给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设计模式来实现:

  1. 创建Page类:首先创建基础类Page,在初始化方法__init__()中定义驱动(driver)、基本的URL(base_url)和超时时间(timeout)等;
    (1)定义open()方法用于打开URL网站;
    (2)关于URL地址的断言部分,则交由on_page()方法来实现;
    (3)find_element()方法用于元素的定位。
  2. 创建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

(0)
上一篇 2024-03-16 09:33
下一篇 2024-03-19 14:26

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信