Python Unittest框架

1、unittest简介

unittest是Python自带的单元测试框架,具备编写用例、组织用例、执行用例、输出报告等自动化框架的条件,主要适用于单元测试,可以用来作自动化测试框架的用例组织执行框架。

2、unittest框架的特性:

  • 提供用例组织与执行:当测试用例只有几条的时候可以不考虑用例的组织,但是当测试用例数量较多时,此时就需要考虑用例的规范与组织问题。unittest单元测试框架就是用 来解决这个问题的。
  • 提供丰富的断言方法:既然是测试,就有一个预期结果和实际结果的比较问题。比较就是通过断言来实现,unittest单元测试框架提供了丰富的断言方法,通过捕获返回值,并且与预期值进行比较,从而得出测试通过与否。
  • 提供丰富的日志:每一个失败用例我们都希望知道失败的原因,所有用例执行结束我们 有希望知道整体执行情况,比如总体执行时间,失败用例数,成功用例数。unittest单元 测试框架为我们提供了这些数据。

3、unittest核心工作原理

unittest的静态类图:

大体流程︰编写TestCase,由TestLoader加载TestCase到TestSuite ,然后由 TextTestRunner来运行TestSuite,最后将运行的结果保存在TextTestResult中。

4、封装前置和后置

(1)setup(),tearDown()(每次执行都会执行一遍)

好处:每次执行一遍

坏处:有100条用例时,会连续执行100次。此时,需要使用setUpClass()和tearDownClass()提高效率,节省时间

用法:[1]使用init实例化浏览器,[2]setup()获取URL、设置等待操作,[3]tearDown()测试完成清除工作,如关闭浏览器和数据库还原等【使用一个类封装此部分代码,然后在运行类中继承类+导入】

(2)setUpClass()和tearDownClass()(多条用例时只会执行一次)【用第二种】

好处:运行消耗比setUp()节省时间

用法:

    1. 新建一个Myunit.py文件,定义TestWebUI类
      1. [1]使用init实例化浏览器
      2. [2] @classmethod装饰器结合setUpClass()方法启动浏览器、获取URL、设置等待操作
      3. [3]@classmethod装饰器结合tearDownClass()方法完成清除工作,如关闭浏览器和数据库还原等【使用一个类封装此部分代码,然后在运行类中继承类+导入】
    1. 新建Test文件,导入Myunit.py模块的所有测试类和方法,继承TestWebUI类,从而实例化浏览器
##2.11.2  前置和后置
import unittest
class TestStringMethods(unittest.TestCase):
    def setUp(self):
        print('每条测试用例开始执行前做的操作.....')
    def test_isupper(self):
        self.assertTrue('FOO'.endswith('O'))
        self.assertFalse('Foo'.isupper())
        print('第一条测试用例')
    def test_strendswich(self):
        self.assertEqual('foo'.endswith('o'), True)
        print('第二条测试用例')
    def tearDown(self):
        print('每条测试用例执行完毕后做的操作.....')
if __name__ == '__main__':
    unittest.main()

-------------------
##2.11.4  setUpClass和tearDownClass
import unittest
from selenium import webdriver
from time import sleep
class TestWebUI(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()
    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()
    def test_QQLogin(self):
        self.driver.get('https://mail.qq.com/cgi-bin/loginpage')
        self.assertEqual(self.driver.title,'登录QQ邮箱')
    def test_MaoyanMovie(self):
        self.driver.get('https://maoyan.com/')
        self.assertEqual(self.driver.title,'猫眼电影 - 一网打尽好电影')
if __name__ == '__main__':
    unittest.main()

5、Unittest组成(存放测试用例)

unittest单元测试中最核心的四个部分是:

  • TestCase(测试用例)
  • TestSuite(测试套件)
  • TestRunner(测试运行器)
  • TestFixture(测试环境数据准备和清理)。
  1. TestCase:一个TestCase的实例就是一个测试用例。而一个完整的测试用例包括测试前准备环境的搭建(setUp)、实现测试过程的代码(run),以及测试后环境的还原(tearDown)。

  1. TestSuite【常用】:一个功能的验证需要多个测试用例,将多个测试用例集合在一起来执行。TestSuite就是用来组装单个测试用例,可以通过addTest方法加载TestCase到TestSuite中,从而返回一个TestSuite实例。

而且 TestSuite也可以嵌套TestSuite。

# 构造测试集:
suite = unittest.TestSuite()
suite.addTest(类名("方法名"))

方法一:使用addTest()把用例逐个添加到suite
在断言case的情况下,if name主模块下添加测试集合。步骤:
● 先创建一个测试集合suite
● 将测试用例一个个使用addTest()把列表添加到suite
● 创建一个runner变量获取unittest.TextTestRunner() ,或者使用HTMLTestRunner(),然后使用run(列表)方法执行所有用例
if __name__=="__main__":
	suite=unittest.Testsuite() 
    suite.addTest(TestCase01('test_06')) 
    suite.addTest(TestCase01('test _04')) 
    suite.addTest(TestCase01('test_02')) 
    suite.addTest(TestCase01('test_05')) 
    suite.addTest(TestCase01('test_01')) 
    suite.addTest(TestCase01('test_07')) 

	runner=unittest.TextTestRunner() 
	runner.run(suite)


方法二:用例放入列表中,再添加到suite
在断言case的情况下,if name主模块下添加测试集合。步骤:
● 先创建一个测试集合suite
● 定义一个列表,创建数组添加所有case(最后执行,按照添加的顺序执行)
● 使用addTests()把列表添加到suite
● 创建一个runner变量获取unittest.TextTestRunner() ,或者使用HTMLTestRunner(),然后使用run(列表)方法执行所有用例
if __name__=="__main__":
    #创建一个测试集合suite
    suite=unittest.Testsuite() 
     #定义列表,把所有用例加到列表上,[类名1("方法名1"),类名2("方法名2")]
    tests=[testCase01('test_06"),testCase01('test_04"),testCase01('test_02"),testCase01('test_05"),testCase01('test_01"),testCase01('test_07")] 
    suite.addTests(tests)
    
    方式一:
    runner=unittest.TextTestRunner() 
    runner.run(suite)

方式二:
 #报告存放的文件夹
	dir_path=r'D:\workspace\seleniumtest'
	#报告命名加上时间格式化
    nowtime=time.strftime("%Y-%m-%d %H-%M-%S",time.localtime())
	#报告绝对路径,打开文件,写入测试结果
    filename = open(dir_path+ nowtime+"/report.html", "wb")
    runner = HTMLTestRunner(stream=filename, verbosity=2, title="自动化测试报告 ",description = "报告详细如下:")
    runner.run(suite)
	#写入文件完成后,关闭文件
	filename.close()

3.TestRunner:在unittest单元测试框架中,通过TextTestRunner类提供的run()方法来执行TestSuite/TestCase。Testrunner可以使用图形界面、文本界面,或返回一个特殊的值等方式来表示测试结果的执行。

  # 运行测试集:
    runner = unittest.TextTestRunner()
	runner.run(suite)

4.TestFixture(测试固件):对一个测试用例环境的搭建和销毁,就是一个fixture,通过覆盖TestCase的setUp()和tearDown()方法来实现。

比如说在测试用例中需要访问数据库,那么可以在 setUp()中建立数据库连接以及进行一些初始化,在tearDown()中清除在数据库中产生的 数据,然后关闭连接。注意tearDown的过程很重要,要为以后的TestCase留下一个干净的环境。

      • 也叫测试夹件,主要工作是【初始化和善后】
      • 测试固件分为两种,一种是类级别的,一种是方法级别的
      • 类级别的测试固件,所有测试用例执行之前与之后运行一次
      • 方法级别的测试固件,每个测试用例执行之前与之后都运行一次
      • 5、通过TestLoader类的discover 方法来【常用】

discover()方法: 由TestLoader类提供,使unittest单元测试框架自动识别测试用例

discover(start_dir, pattern='test*.py', top_level_dir=None):找到指定目录下所有测试模块,并可递归查到子目录下的测试模块,只有匹配到测试用例文件名才能被加载到测试集。如果启动的不是顶层目录,那么顶层目录必须单独指定。

(1)start_dir:要测试的模块名或测试用例目录

假设有测试目录,对于这样的目录结构,若将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

(2)pattern='test*.py':表示用例文件名的匹配规则。此处匹配文件名以"test"开头的".py"类型的文件,"*"表示任意多个字符

(3)top_level_dir=None:测试模块的顶层目录,如果没有顶层目录,默认为None

解释:discover()方法会自动根据测试目录(test dir)匹配查找测 试用例文件(test*.py),并将查找到的测试用例组装到测试套件 中,因此,可以直接通过run()方法执行discover,大大简化了 测试用例的查找与执行。

# 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()

方法二:实现直接执行,不需要一个个方法导入。等同于,添加一个集合,执行case。
#coding=utf-8 
import sys,os,unittest
case_path=os.getcwd()+"/UnittestCase/"
print(case_path)

discover=unittest.defaultTestLoader.discover(case_path,pattern='test_*.py')
#unittest.TextTestRunner().run(discover) 

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()

方法三:封装一个方法,再直接调用
import os,time,unittest
from HTMLTestRunner import HTMLTestRunner

def getAllCases():
	'''获取tesTcase下面的所有测试模块'''
	Testsuite = unittest.defaultTestLoader.discover(
		start_dir=os.path.join(os.path.dirname(__file__),'TestCases'),
		pattern='test*.py')
	return Testsuite

def RunMain():
	'''生成测试报告写入指定Reports目录'''
    now=time.strftime("%Y_%m_%d_%H_%M_%S")
	fp=open(os.path.join(os.path.dirname(__file__),'report', now + 'report.html'),'wb')
	runner=HTMLTestRunner(stream=fp,title='Python+Selenium自动化测试实战',
				   description='基于python语言PO自动化测试')
    runner.run(getAllCases())

if __name__ == '__main__':
   RunMain()

6、断言方法

断言即在测试用例执行过程中,通过判断测试得到实际结果和预期结果是否相等。

断言方法包括:

  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的实例
#coding=utf-8 
import requests 
import unittest 
url="http://www.imooc.com"
data={
 "username":"1111", 
"password":"22222"
}
class TestCase1(unittest.TestCase):
    def setUp(self):
        print("case开始执行")
		self.driver = webdriver.Chrome()
    def tearDown(self):
        print("case结束执行")
		self.driver.quit()

    @classmethod 
    def setUpClass(c1s):
        print("case类开始执行")
    @classmethod 
    def tearDownClass(c1s):
        pass #无执行内容时,使用pass直接执行略过
            #print("case类执行结束")
    '#使用方法时要查看判断的类型是什么,当前是dict,这个会执行失败

	#assertEqual
    def test_QQLogin(self):
        self.driver.get('https://mail.qq.com/cgi-bin/loginpage')
        self.assertEqual(self.driver.title,'登录QQ邮箱','页面跳转失败,请重新检查;')
	 def test_05(self):
        flag="111"
        flag1="2222"
        self.assertEqual(flag,flag1) 
	
	#assertIn
	def test_06(self):
        flag="adfadfadfadfadsfaqeewr"
        s="fads"
        self.assertIn(s,flag,msg="不等于true")
        print("06")
        
    def test_01(self):
        res =requests.get(url=url,params=data).json()
        #data1和data的值不一样    
        data1={ "user":"11111"
        }
        self.assertDictEqual(res,data1)
    '''#可以在断言处增加msg,打印'''       
    def test_07(self):
        data1={
        "username":"111111", 
        "password":"22222"
        }
        self.assertDictEqual(data,data1,msg="这2个方法不相同")
    def test_03(self):
        flag=True 
        self.assertFalse(flag)
    def test_04(self):
        flag=False
        self.assertTrue(flag)
   
if __name__=="__main__":
    unittest.main()

注意:

(1)assertIn()判断方式与断言执行效果一样的,如:

if "username"and "password"in res:

print(“执行成功")

(2)注意断点方法根据变量的类型设置,断言方法包括:看上方例子

(3)断言方法处可以使用msg,可以打印对应文字

7、unittest执行测试用例的顺序

默认根据ASCII码的顺序加载测试用例,数字与字母的顺序为:0-9,A-Z,a-z。对于测试目录与测试文件也是一样的加载顺序,只能通过测试用例的命名来提高被执行的优先级。

如:用例执行顺序。在代码中test sub方法写在test add前,但实际,test add比test sub 先运行。为什么呢? unittest执行 测试用例,默认是根据ASCII码的顺序加载测试用例, 数字与字母的顺序为: 0-9,A-Z,a-Z。

8、跳过测试和预期失败

若想让某个测试用例不执行,有没有办法呢?当然是有的,可以使用skip装饰器。

unittest提供了如下装饰器:

unittest.skip(reason):无条件的跳过装饰的测试,说明跳过测试的原因

unittest.skipIf(condition, reason):如果条件(condition)为真,跳过装饰的测试

unittest.skipUnless(condition, reason):如果条件(condition)为假,跳过装饰的测试

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):
	......

9、数据驱动DDT+Excel+yaml

  1. 数据驱动和关键驱动的区别:
  • Data-Driven Tests(DDT)即数据驱动测试,可以实现不同数据运行同一个测试用例。ddt本质其实就是装饰器,一组数据一个场景。
  • 关键字驱动(核心:把业务逻相封装成关键字login,只需要调用login。)
  1. 混合驱动模式(关键字驱动+数据驱动)
  2. 在进行数据驱动测试实战中,需要在测试类上使用@ddt.ddt装饰器,在测试用例上使用@ddt.data装饰器。@ddt.data装饰器可以把参数作为测试数据,参数可以是单个值、列表、元组或字典。对于列表和元组,需要使用@ddt.unpack装饰器把元组和列表解析成多个参数。

在python里面装饰器是以@开头,并且装饰有两种:类装饰器,函数装饰器。

ddt里面有哪些装饰器:

    • @ddt(类装饰器,申明当前类使用ddt框架)
    • @data(函数装饰器,用于给测试用例传递数据),支持传python所有数据类型:数字(int,long,float,compix),字符串,列表1ist,元祖tuple,集合
      • 当传单个值时,所有的类型都可以传,并且用例执行一次。
      • 当传入多个值的时候,那么传几个值,那么用例执行几次。
    • @unpack(函致装饰器,将传输的数据包解包),一般作用于元祖tuple和列表list.
      • 如果是数字或者字符串:那么不需要@unpack
      • 如果是元祖和列表的话,那么可以通过@unpack,但是参数的个数必须和解完包后的值的个数一样
      • 如果是集合无法解包:参考上方例子(需要多个值来传)
      • 如果是字典,那么可以通过@unpack解包,但是参数的名字和个数必须和字典的键保持一致
    • @fle_data(函数装饰器,可直接读取yaml/json文件)
    • @data([a,d],[c,d])
    • 如果没有@unpack,那么[a,b]当成一个参数传入用例运行
    • 如果有@unpack,那么[a,b]被分解开,按照用例中的两个参数传递

##2.12.2  DDT在自动化测试中的应用(传列表)

import ddt
import unittest

# 给4条测试数据
    Testdata = [
        {"username": "admin", "password": "123456", "excepted": {'code': '200', 'msg': '登录成功'}},
        {"username": None, "password": "1234567", "excepted": {'code': '400', 'msg': '用户名或密码不能为空'}},
        {"username": "admin", "password": None, "excepted": {'code': '400', 'msg': '用户名或密码不能为空'}},
        {"username": "admin", "password": "123456789", "excepted": {'code': '404', 'msg': '用户名或密码错误'}},
    ]
@ddt.ddt
class TestModules(unittest.TestCase):
    def setUp(self):
        print('testcase beaning....')
    def tearDown(self):
        print('testcase ending.....')
    @ddt.data(*Data)
    def test_DataDriver(self,Data):
        #print('DDT数据驱动实战演示:',Testdata)
        res = login.login_check(Testdata['username'], Testdata['password'])
        self.assertEqual(res, Testdata['excepted'])
if __name__ == '__main__':
    unittest.main()


-----------------------------
import ddt
import unittest

# 给4条测试数据
def readData():
    Testdata = [
        {"username": "admin", "password": "123456", "excepted": {'code': '200', 'msg': '登录成功'}},
        {"username": None, "password": "1234567", "excepted": {'code': '400', 'msg': '用户名或密码不能为空'}},
        {"username": "admin", "password": None, "excepted": {'code': '400', 'msg': '用户名或密码不能为空'}},
        {"username": "admin", "password": "123456789", "excepted": {'code': '404', 'msg': '用户名或密码错误'}},
    ]
	return TestData

@ddt.ddt
class TestModules(unittest.TestCase):
    def setUp(self):
        print('testcase beaning....')
    def tearDown(self):
        print('testcase ending.....')
    @ddt.data(*readData())
    def test_DataDriver(self,Data):
        #print('DDT数据驱动实战演示:',Testdata)
        res = login.login_check(Testdata['username'], Testdata['password'])
        self.assertEqual(res, Testdata['excepted'])
if __name__ == '__main__':
    unittest.main()

DDT+Excel表格自动化使用

  1. 使用xlsx测试思路

首先安装xlrd,pip install xlrd

    1. 创建一个xlsx文件,data.xlsx
    2. 导入os、unittest、HTMLTestRunner、ddt、selenium
    3. 使用@ddt在类名上
    4. 编写登录用例def test_jenkins login(self):
    5. 编写read_excel()函数读取excel
    6. 使用@data(*read excel())传值,获取到excel的内容
    7. 使用@unpack解包,获取各种值

注意:

@data(read excel())打印的结果是[[1,‘admin',123456],[2,'admin",'admin],[3,123456,123456]]会带多一个[],@data(*read excel())带个*号则解决[]问题,能执行三次

###2.12.4  Excel整合DDT自动化测试实战(这种方式是遍历表格打印)###2.12.3  Excel自动化测试实战(这种方式是一个个行读取)

import xlrd
import ddt,unittest
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait   #导入WebDriverWait类
from selenium.webdriver.support import expected_conditions as EC  # 导入EC模块
def readUserName(row):
    '''读取用户名'''
    book  = xlrd.open_workbook('datainfo.xlsx','r')
    table = book.sheet_by_index(0)
    return table.row_values(row)[0]
def readPasswd(row):
    '''读取用户名'''
    book  = xlrd.open_workbook('datainfo.xlsx','r')
    table = book.sheet_by_index(0)
    return table.row_values(row)[1]
def readAssertText(row):
    '''读取预期结果'''
    book  = xlrd.open_workbook('datainfo.xlsx','r')
    table = book.sheet_by_index(0)
    return table.row_values(row)[2]



 class TestSouHuLogin(unittest.TestCase):
    def  setUp(self):
        self.driver = webdriver.Chrome()
        self.testUrl = "https://mail.sohu.com/fe/#/login"
    def  tearDown(self):
        self.driver.quit()
    def  by_css(self,usernameloc):
        '''重写css定位'''
        return self.driver.find_element_by_css_selector(usernameloc)
    def  getassertText(self):
        '''获取验证信息'''
        try:
            sleep(2)
            loctor = (By.CSS_SELECTOR,'.tipHolder.ng-binding')
            WebDriverWait(self.driver, 5, 0.5).until(EC.presence_of_element_located((loctor)))
            return self.by_css('.tipHolder.ng-binding').text
        except Exception as message:
            print('元素定位报错!报错原因是:{}'.format(message))
    def  souhuLogin(self,user,passwd):
        '''封装登录功能'''
        self.by_css('.addFocus.ipt-account.ng-pristine.ng-valid').send_keys(user)
        self.by_css('.addFocus.ng-pristine.ng-valid').send_keys(passwd)
        self.by_css('.btn-login.fontFamily').click()
    def  test_souHuLogin_001(self):
        '''账号和密码为空:登录失败'''
        self.driver.get(self.testUrl)
        sleep(3)
        self.souhuLogin(readUserName(1),readPasswd(1))
        self.assertEqual(self.getassertText(), readAssertText(1))
    def  test_souHuLogin_002(self):
        '''账号正确和密码为空:登录失败'''
        self.driver.get(self.testUrl)
        self.souhuLogin(readUserName(2), readPasswd(2))
        self.assertEqual(self.getassertText(), readAssertText(2))
    def  test_souHuLogin_003(self):
        '''账号错误和密码为空:登录失败'''
        self.driver.get(self.testUrl)
        self.souhuLogin(readUserName(3), readPasswd(3))
        self.assertEqual(self.getassertText(), readAssertText(3))
    def  test_souHuLogin_004(self):
        '''账号为空和密码正确:登录失败'''
        self.driver.get(self.testUrl)
        self.souhuLogin(readUserName(4), readPasswd(4))
        self.assertEqual(self.getassertText(), readAssertText(4))
if __name__ == '__main__':
    unittest.main()
'''方法一思路:
引入ddt模块,在测试类上增加@ddt.ddt装饰器,在测试用例上增加@ddt.data、@ddt.unpack方法。其中@ddt.data 方法用于把newRows 参数作为测试数据,newRows 参数实际是一个嵌套的列表;@ddt.unpack方法用于把测试数据分解为多个值并且作为实际参数传入测试用例中的 user、passwd和 text中。getassertText()方法用来获取页面实际文本信息,然后与text预期文本进行比较,验证信息一致则通过。
'''
import xlrd
import ddt,unittest
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait      #导入WebDriverWait类
from selenium.webdriver.support import expected_conditions as EC  #导入EC模块
'''
readData()方法用于读取datainfo.xlsx表中每一行的用户名、密码和文本信息,然后将读取结果分别依次追加到newRows。
'''
def readData():
    book  = xlrd.open_workbook('datainfo.xlsx','r')  #读取datainfo.xlsx表
    table = book.sheet_by_index(0)               #获取第一个sheet
    newRows=[]      #传所有的行和列,则所有数据会打印成一行             
    for rowValue in range(1,table.nrows):
        newRows.append(table.row_values(rowValue,0,table.ncols))
    return newRows             #返回新的newRows


@ddt.ddt
class TestLogin(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()
        self.testUrl = "https://mail.sohu.com/fe/#/login"
    def tearDown(self):
        self.driver.quit()
    def by_css(self,usernameloc):
        '''重写css定位'''
        return self.driver.find_element_by_css_selector(usernameloc)
    def getassertText(self):
        '''获取验证信息'''
        try:
            sleep(2)
            loctor = (By.CSS_SELECTOR,'.tipHolder.ng-binding')
            WebDriverWait(self.driver, 5, 0.5).until(EC.presence_of_element_located((loctor)))
            return self.by_css('.tipHolder.ng-binding').text
        except Exception as message:
            print('元素定位报错!报错原因是:{}'.format(message))
    @ddt.data(*readData())
    @ddt.unpack
    def test_souhuLogin(self,user,passwd,text):
        self.driver.get(self.testUrl)
        sleep(3)
        self.by_css('.addFocus.ipt-account.ng-pristine.ng-valid').send_keys(user)
        self.by_css('.addFocus.ng-pristine.ng-valid').send_keys(passwd)
        self.by_css('.btn-login.fontFamily').click()
        self.assertEqual(self.getassertText(),text)
if __name__ == '__main__':
    unittest.main()


方式二:这种方式读取表格时使用了读取所有行和列,验证2种方式其中哪种适合
import os 
import unittest 
from HTMLTestRunner import HTMLTestRunner 
from ddt import ddt,data,unpack 
from selenium import webdriver 

#读取exce1 
#@data([”百里",“微微“],[“18",“21]) 
#x1rd,x1wt,openpyx1 
def read_excel():
    workbook=openpyxl.load_workbook("data.x1sx") 
    sheet=workbook["login"] 
    #print(sheet.max_row,sheet.max_column) 
    allList=[]#传所有的行和列,则所有数据会打印成一行 
    for row in range(2,sheet.max row+1):#行数 ,row+1才能获取下一行
        tempList=[] #传所有的行和列,但是数据会分行打印
        for col in range(1,sheet.max column+1):
            tempList.append(sheet.ce11(row,col).value) 
            #print(tempList)
         allList.append(tempList) 
      #print(allList)
      return allList
 
@ddt
class Test(unittest.Testcase):
    
@data(*read_excel())
@unpack
def test_jenkins login(self,order,username,password):
        #print(args)

    driver=webdriver.Chrome() 
    driver.get("htto://1ocalhost:8080/1ogin?from=82E") 
    driver.find element by id("j username").send keys(username) 
    driver.find element by name("j password").send keys(password) 
    driver.find_element _by_name("Submit").click() 

#此处是只是传具体值
#@data(*read excel())
#def test_jenkins login(self,args):
#        #print(args)

#    driver=webdriver.Chrome() 
#driver.get("htto://1ocalhost:8080/1ogin?from=82E") 
#driver.find element by id("j username").send keys("admin") 
#driver.find element by name("j password").send keys("123456") 
#driver.find_element _by_name("Submit").click() 
 
#if __name__ =='__main__':
#    unittest.main()

#使用html方式打印报告
if __name__ =='__main__':
testcases=unittest.defaultrestLoader.discover (os.getcwd(),"*.py") 
filename=open(os.getcwd()+"/report.html","wb") 
runner=HTMLTestRunner(stream=filename,verbosity=2,title="自动化测试报告 ",description="报告详细如下:")
runner.run(testcases)

DDT+Yaml自动化使用

import yaml
import unittest
from selenium import webdriver
from  selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from ddt import ddt, data, unpack, file_data
from HTMLTestRunner import HTMLTestRunner
import os
import yaml
import time

@ddt
class TestCase(unittest.TestCase):
# 测试用例
    @file_data("./data/test.yaml")
    def test_search(self,txt):
        self.driver = webdriver.Chrome()
        self.driver.get('https://www.baidu.com/')
        self.driver.find_element(By.ID,'kw').send_keys(txt)
        self.driver.find_element(By.ID, 'su').click()

if __name__=="__main__":
    # 实例化测试套件
    suite = unittest.TestSuite()
    # 加载测试用例
    #testcases=unittest.defaultTestLoader.discover(os.getcwd(),"*.py")
    testcases=[TestCase("test_search")]
    suite.addTests(testcases)
    # 获取当前时间
    now = time.strftime("%Y-%m-%d %H_%M_%S")
    # 定义报告存放路径
    filename = now+open(r'D:\workspace\seleniumtest'+ "/report.html", "wb")
    # 定义测试报告
    runner = HTMLTestRunner(stream=filename, verbosity=2, title="自动化测试报告 ",description = "报告详细如下:")
    runner.run(suite)
    filename.close()# 关闭报告文件

Yaml自动化使用

YAML是一种直观的能够被计算机识别的数据序列化格式,容易阅读,并且容易和脚本语言交互。YAML类似于XML,但是语法比XML简单得多:而对于JSON,YAML可以写成规范化的配置文件。此外,不管是做Web自动化测试还是接口自动化测试,都可以使用YAML来管理测试数据,这种方法也比较简单高效。

官网:The Official YAML Web Site

在线写yaml文件:YAML、YML在线编辑器(格式化校验)-BeJSON.com

使用基本规则

1. 大小写敏感

2. 使用缩进表示层级关系

3. 缩进时不允许使用Tab,只允许使用空格

4. 缩进的空格数目不重要,只要相同层级的元素左对齐即可

5. # 表示注释,从它开始到行尾都被忽略

实现思路:

1、YAML的安装非常简单,可以直接在线使用pip命令进行安装。打开cmd命令提示符界面,输入“pip install pyyaml”进行在线安装

2、YAML在自动化测试中使用data.yaml文件存储的数据名必须是以.yaml结尾。

3、导入YAML模块import yaml,readYaml()方法下的open()方法用于打开data.yaml文件,为了防止乱码可使用encoding="utf-8"来声明。yaml.load()方法用于读取data.yaml文件下的所有数据。close()方法用于关闭整个yaml文件,如果不关闭会产生ResourceWarning提示

注意:

  • 通过yaml自身的函数形态来读取单个内容:yaml.load(stream=file,Loader=yaml.FullLoader)
  • 通过yaml自身的函数形态来读取多个内容:yaml.loadAll(stream=file,Loader=yaml.FullLoader)

data.yaml

userNull:
 username:""
 password:""
 assertText:"请输入账号密码"
passwordNull:
 username:"admin"
 password:""
 assertText:"请输入账号密码"
####2.12.5  YAML自动化测试实战
import unittest,yaml
from time import sleep
from selenium import webdriver
def readYaml():
   '''获取所有yaml所有数据'''
   f = open('./data/data.yaml', 'r', encoding='utf-8')
   data = yaml.load(f)
   #print(data)
   f.close()
   return data
class TestLogin(unittest.TestCase):
   def setUp(self):
      self.driver = webdriver.Chrome()
      self.testUrl = "https://mail.sohu.com/fe/#/login"
   def tearDown(self):
      self.driver.quit()
   def by_css(self,usernameloc):
      '''重写css定位'''
      return self.driver.find_element_by_css_selector(usernameloc)
   def getassertText(self):
      '''获取验证信息'''
      try:
         return self.by_css('.tipHolder.ng-binding').text
      except Exception as message:
         print('元素定位报错!报错原因是:{}'.format(message))
   def souhuLogin(self,user,passwd):
      '''封装登录功能'''
      self.by_css('.addFocus.ipt-account.ng-pristine.ng-valid').send_keys(user)
      self.by_css('.addFocus.ng-pristine.ng-valid').send_keys(passwd)
      self.by_css('.btn-login.fontFamily').click()
   def test_souHuLogin_001(self):
      '''账号正确和密码为空:登录失败'''
      self.driver.get(self.testUrl)
      sleep(3)
      self.souhuLogin(readYaml()['userNull']['username'],readYaml()['userNull']['password'])
      self.assertEqual(self.getassertText(), readYaml()['userNull']['assertText'])
   def test_souHuLogin_002(self):
      '''账号错误和密码为空:登录失败'''
      self.driver.get(self.testUrl)
      sleep(3)
      self.souhuLogin(readYaml()['passNull']['username1'],readYaml()['passNull']['password1'])
      self.assertEqual(self.getassertText(), readYaml()['passNull']['assertText1'])
if __name__ == '__main__':
   unittest.main()

https://www.yuque.com/mengxiaoqi-lrqrz/ylw9z8/hft8wa

10、生成HTML测试报告

1、下载文件:

下载地址:HTMLTestRunner - tungwaiyip's software  ,右键HTMLTestRunner.py文件另存为即可。

下载后放到python安装目录/Lib下,如我的路径为:C:\Users\XXX\AppData\Local\Programs\Python\Python36\Lib

2、修改的内容如下

第94行, 将import StringIO修改成import io
第539行,将self.outputBuffer = StringIO.StringIO()修改成self.outputBuffer = io.StringIO()
第642行,将if not rmap.has_key(cls):修改成if not cls in rmap:
第631行,将print >> sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime)修改成print(sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime))
第766行,将uo = o.decode('latin-1')修改成uo = e
第775行,将ue = e.decode('latin-1')修改成ue = e

3、使用HTMLTestRunner

方法一、使用TestLoader加载所有用例HTMLTestRunner(使用html报告运行所有测试用例

  1. 导入os、unittest、HTMLTestRunner
  2. 创建Testsuite获取testcase下面所有测试用例,discover()方法会自动根据测
  3. 创建一个filename变量,使用open()方法定义测试报告文件存放路径,创建并打开测试报告文件
  4. 定义一个runner变量定义测试报告,并用runner()方法执行集中的用例,保存到html报告

HTMLTestRunner()参数:

    • stream:默认值是sys.stderr,表示默认将结果输出到控制台,可以配置报告路径(但是要先用open()方法打开文件,是以一种文件流的方式),输出到指定位置;
    • verbosity是一个选项,表示测试结果的信息复杂度,有0、1、2三个值。verbosity=0 : 你只能获得测试用例数总的结果;verbosity=1(默认模式):在每个成功的用例前面有 个“.”每个失败的用例前面有个“F”; verbosity=2 (详细模式):测试结果会显示每个测试用例的所有相关的信息。
    • title:标题
    • descriptions:默认值为True,描述内容

不常用参数

    • output:是输出的文件夹,如果没有定义日志存放位置,日志报告会以html为后缀自动放在“工程目录/reports/file_d”这个路径的文件夹下,并以当前时间命名
    • combine_reports:默认值为False,将值设置为True可将测报告合并
    • report_name:报告名称,report_name会自动加上时间后缀,时间格式为%Y-%m-%d-%H-%M-%S

解析:fp=open(获取当前路径,'存放报告地址,当前时间+ 'report.html'),'wb')

方式一:定义一个方法【常用】封装
'''getAIlCasesO方法下的 discoverO函数用于读取TestCases目录下以 test 开头的·py文件,并这
回所有测试模块下的测试用例。RunMain()方法用于生成测试报告,并将测试结果写入测试报告中。
wb模式用于读取二进制文件。time.strfimeO方法用于获取系统的当前时间,以便区分生成的不同
测试报告名。
'''
import os,time,unittest
from HTMLTestRunner import HTMLTestRunner


def getAllCases():
	'''获取tesTcase下面的所有测试模块'''
    start_dir=os.path.join(os.path.dirname(__file__)
	Testsuite = unittest.defaultTestLoader.discover(start_dir,'TestCases'),
	pattern='test*.py')
	return Testsuite

def RunMain():
	'''生成测试报告写入指定Reports目录'''
	fp=open(os.path.join(os.path.dirname(__file__),'report',time.strftime("%Y_%m_%d_%H_%M_%S")+ 'report.html'),'wb')
	HTMLTestRunner(stream=fp,title='Python+Selenium自动化测试实战',
				   description='基于python语言PO自动化测试').run(getAllCases())

if __name__ == '__main__':
   RunMain()


方式二:
import unittest
from HTMLTestRunner import HTMLTestRunner
import time
if __name__ == '__main__':
    #指定批量执行的模块
    test_module = './'
    discover = unittest.defaultTestLoader.discover(test_module, pattern="test*.py")

    #报告存放的文件夹
    dir_path='./'
    #报告命名加上时间格式化
    now=time.strftime('%Y-%m-%d %H_%M_%S')
    #报告绝对路径
    report_path=dir_path +now +' result.html'
    #打开文件,写入测试结果
    with open(report_path,'wb') as f:
        runner=HTMLTestRunner(stream=f,verbosity=2,title='Math测试报告',description='用例执行详细信息')
        runner.run(discover)
    f.close()

方法二、添加测试集使用HTMLTestRunner(使用html报告运行所有测试用例

  1. 导入os、unittest、HTMLTestRunner
  2. 创建测试集,向测试集组装测试用例:
    • 在【if _name_主模块】下先创建一个测试集suite(记得引用unittest+测试类模块)
    • 创建一个数组【把所有用例按照顺序添加到数组】,【添加方式:测试类类名(测试方法名)】(最后执行,按照添加的顺序执行)
    • 使用addTests()添加数组到测试集
  1. 创建一个filename变量,使用open()方法定义测试报告文件存放路径,创建并打开测试报告文件
  2. 定义一个runner变量定义测试报告,并用runner()方法执行集中的用例,保存到html报告

HTMLTestRunner()参数:

    • stream:默认值是sys.stderr,表示默认将结果输出到控制台,可以配置报告路径(但是要先用open()方法打开文件,是以一种文件流的方式),输出到指定位置;
    • verbosity:=1时,默认值为1,不限制完整结果,即单个用例成功输出’.’,失败输出’F’,错误输出’E’;=0的时候,不输出信息;=2的时候,需要打印详细的返回信息;
    • title:标题
    • descriptions:默认值为True,描述内容

不常用参数

    • output:是输出的文件夹,如果没有定义日志存放位置,日志报告会以html为后缀自动放在“工程目录/reports/file_d”这个路径的文件夹下,并以当前时间命名
    • combine_reports:默认值为False,将值设置为True可将测报告合并
    • report_name:报告名称,report_name会自动加上时间后缀,时间格式为%Y-%m-%d-%H-%M-%S

使每次生成的测试报告名称都不重复且有意义,最好的办法是在报告名称中加入当前时间,这样生成的报告既不会重叠,也能更清晰的知道报告的生成时间。

import time :导入time模块

  1. time.time():获取当前时间戳
  2. time.ctime():当前时间的字符串形式
  3. time.localtime():当前时间的struct_time形式
  4. time.strftime:用来获得当前时间,可将时间格式化为字符串。

import os 
import unittest 
from HTMLTestRunner import HTMLTestRunner 

class Test(unittest.Testcase):
    def test01_baili(self):
    	print(“测试百里")

if __name__=="__main__":
    #1.创建用例集
    suite = unittest.TestSuite()
    #testcases=unittest.defaultTestLoader.discover(os.getcwd(),"*.py")
    #2.定义数组变量
    testcases=[RunCase("test_01_login"),RunCase("test_02_login")]
    #3.使用addTests添加数组变量
    suite.addTests(testcases)

    #报告存放的文件夹
	dir_path=r'D:\workspace\seleniumtest'
	#dir_path=os.path.join(os.path.dirname(__file__)
	#报告命名加上时间格式化
    nowtime=time.strftime("%Y-%m-%d %H-%M-%S",time.localtime())
	#报告绝对路径,打开文件,写入测试结果
    filename = open(dir_path+ nowtime+"/report.html", "wb")
    runner = HTMLTestRunner(stream=filename, verbosity=2, title="自动化测试报告 ",description = "报告详细如下:")
    runner.run(suite)
	#写入文件完成后,关闭文件
	filename.close()

生成的测试报告如下:

9-Unittest+HTMLTestRunner不能生成报告解决方法

https://www.cnblogs.com/santiandayu/p/10004179.html

参考:

unittest + HTMLTestRunner_unittest testrunner_你们的好朋友大强的博客-CSDN博客

11、发送邮件实战

1、SMTP

###2.13.1  纯文本的邮件实战
import smtplib     #调用smtp发件服务
from email.mime.text import MIMEText    #导入做纯文本的邮件模板类
smtpsever='smtp.qq.com'                #QQ邮箱服务器
sender='qq邮箱账号@qq.com'           #发送者邮箱
psw="hcygozfxeassddhhb"              #配置邮箱客户端生成的QQ邮箱授权码
receiver='126邮箱账号@126.com'       #接收者邮箱
port=465   #QQ邮箱服务器默认端口号

msg=MIMEText(body,'html','utf-8')     #邮件正文内容
msg['from']=qq邮箱账号@qq.com'     #发送者账号
msg['to']='126邮箱账号@qq.com'      #接收者账号
msg['subject'] = "这个是纯文本发送的邮件示例"

smtp = smtplib.SMTP_SSL(smtpsever,port)  #调用发件服务
smtp.login(sender,psw)   #通过发送者的邮箱账号和授权码登录邮箱
smtp.sendmail(sender,receiver,msg.as_string())  #发送邮件,信息以字符串方式保存
smtp.quit()                                   #关闭邮件服务


###2.13.2  带附件的邮件实战
import smtplib
from email.mime.text import MIMEText           #导入做纯文本的邮件模板类
from email.mime.multipart import MIMEMultipart   #导入MIMEMultipart类
#发邮件相关参数
smtpsever='smtp.qq.com'       #QQ邮箱服务器
sender='239xxxxx@qq.com'    #发送者邮箱
psw="xxxxxxxxxxxxxxxx"      #qq邮箱授权码
receiver='xxxxx@126.com'    #接收者邮箱账号
port=465                   #QQ邮箱服务器默认端口号

filepath=r"./readme.txt"  #编辑邮件的内容
with open(filepath,'rb') as fp:    #读文件
    mail_body=fp.read()


#主题
msg=MIMEMultipart()
msg["from"]=sender
msg["to"]=receiver
msg["subject"]="带附件的邮件发送模版主题"

body=MIMEText(mail_body,"html","utf-8")
msg.attach(body)
att = MIMEText(mail_body,"base64","utf-8")
att["Content-Type"] = "application/octet-stream"
att["Content-Disposition"] = 'attachment; filename="test_report.html"'
msg.attach(att)


try:
    smtp=smtplib.SMTP()
    smtp.connect(smtpsever)                     #连接QQ邮箱服务器
    smtp.login(sender,psw)                       #调用发件服务
except:
    smtp=smtplib.SMTP_SSL(smtpsever,port)
    smtp.login(sender,psw)                       #登录邮箱
smtp.sendmail(sender,receiver,msg.as_string())     #发送邮件
smtp.quit()

12、持续集成

  1. 安装jenkins
  2. 构建一个自由风格的任务
  3. 点击构建-基于window环境-选择Execute Windows batch command+填写命令:

D:

cd D:\workspace

C:\python\python.exe allTests.py;

保存,配置完成;

  1. 点击项目-Build now-构建成功,查看终端日志
  2. 使用HTML Publisher plugin输出报告或者Allure输出报告(参考步骤8)

13、框架:测试框架封装和脚本的分层设计

实现思路:

  • 使用POM模式封装测试框架:封装页面对象、基础层,测试用例,定义common封装公共方法包括截图、读取xlxs表格、日志,然后在写测试用例的时候可以调用截图、日志、用例等,最后runAllCases.py使用TestLoader类获取所有用例和使用HTMLTestRunner执行用例生成报告。
  • 上传代码到GIT:git add * , git commit -m '提交', git push origin test
  • 持续集成jenkins执行用例:配置源码管理git、window执行python runAllCases.py【执行方式:每日定时执行、发版前执行、冒烟测试执行(提测前执行)】,节点电脑接上手机
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Python unittest requests框架是一种用于编写自动化测试的工具,它基于Pythonunittest框架和requests库。使用该框架可以方便地进行HTTP请求的测试,包括请求的参数、请求头、请求体、响应状态码、响应头和响应体等方面的测试。同时,该框架还支持多种断言方式,可以对测试结果进行验证。使用Python unittest requests框架可以提高测试效率和测试质量,是自动化测试中常用的工具之一。 ### 回答2: Python unittestPython标准库中的一个单元测试框架,它提供了一种编写、运行和组织测试用例的方式。unittest库可以用于测试Python代码中的函数、类和模块。 而requests是Python中一个常用的第三方库,可以用于发送HTTP请求和处理HTTP响应。requests库提供了简洁的API,使得编写HTTP请求和处理响应变得更加方便和易懂。 当我们使用unittest来编写测试用例时,可以结合requests库来测试我们的接口或Web应用程序。以接口测试为例,我们可以使用requests库发送HTTP请求,并验证服务器返回的HTTP响应是否符合我们的预期。 在测试中,我们可以使用unittest库的TestCase类来创建测试用例,然后使用requests库发送HTTP请求并获取响应。我们可以断言响应的状态码、内容,甚至可以模拟不同的请求方式和参数来测试接口的不同情况。 通过使用unittest和requests框架,我们可以编写清晰、可重复运行的测试用例,对接口进行全面的自动化测试,提高代码的质量和稳定性。同时,unittest还提供了用例的组织和运行的功能,可以方便地执行和管理测试用例。 总之,Pythonunittest和requests框架是一个对接口进行自动化测试非常方便和强大的工具组合,它们可以使我们的测试工作更加高效、准确,并且能够有效地帮助我们发现潜在的问题,提高软件质量。 ### 回答3: Python unittestPython标准库中的一个模块,它是一种单元测试框架,可以用于编写和执行测试用例unittest提供了一些类和方法,方便我们编写测试用例,执行测试和生成测试报告。 而requests是Python用于发送HTTP请求的第三方库,它提供了简洁而强大的API,使得发送HTTP请求变得更加方便和灵活。 结合两者,我们可以通过unittest对requests模块进行单元测试。在测试用例中,我们可以使用requests库发送HTTP请求,并对返回的响应进行断言,以验证请求的正确性和响应的准确性。 样例如下: ```python import unittest import requests class TestRequest(unittest.TestCase): def test_get(self): url = 'http://example.com' response = requests.get(url) self.assertEqual(response.status_code, 200) self.assertIn('Example Domain', response.text) def test_post(self): url = 'http://example.com' data = { 'username': 'testuser', 'password': 'testpassword' } response = requests.post(url, data=data) self.assertEqual(response.status_code, 200) self.assertEqual(response.json()['success'], True) if __name__ == '__main__': unittest.main() ``` 上述代码定义了一个测试类`TestRequest`,其中包含了两个测试方法`test_get`和`test_post`。这两个方法分别测试了使用requests发送GET和POST请求,并对响应结果进行断言。`self.assertEqual`用于断言两个值相等,`self.assertIn`用于断言一个值是否在另一个值中。 最后,使用`unittest.main()`执行测试用例,并输出测试结果。 通过这样的单元测试,我们可以确保我们使用requests发送的HTTP请求是正确的,响应也是符合预期的。这样可以提高代码的稳定性和可靠性,减少潜在的错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值