Ⅷ python单元测试

 

1.小结

 

2.单元测试

1.unittest框架

1.1)四个核心概念

· TestCase:一个testcase的实例就是一个测试用例

· TestSuite:多个测试用例集合在一起。TestLoader:是用来加载TestCase到TestSuite中的

· TestRunner:用来执行测试用例的。

· fixture:测试用例环境的搭建和销毁。测试前准备环境的搭建(setUp),执行测试代码(run),以及测试后环境的还原(tearDown)

 

1.2)单元测试案例

·测试需求1

 

myCode.py

 

def counter(a, b, method):
   """
  计算两个整数加减乘除的计算器
  :param a: 数值1
  :param b: 数值2
  :param method: 数值的运算方法
  :return: 运算的结果
  """
   # 判断a,b是否是int类型
   if isinstance(a, int) and isinstance(b, int):
       if method == "+":
           return a + b
       elif method == "-":
           return a - b
       elif method == "*":
           return a * b
       elif method == "/":
           return a / b
       else:
           return '计算方式有误'
   else:
       return '参数a和b只能为整数类型的数据'

"""

正向用例:

1、加
入参:11,22,+
预期结果:33
2、减
入参:11,22,-
预期结果:-11
3、乘
入参:2,10,*
预期结果:20
4、除
入参:20,10,/
预期结果:2.0

反向用例:
5、传入的计算方式:不是加减乘除
入参: 11,22 , a
预期结果:计算方式有误
6、传入的数值非整数类型
入参:11.3,99,+
预期结果:参数a和b只能为整数类型的数据
7、。。。
8、。。。


"""
# 手工用例:
if __name__ == '__main__':
  res = counter(11, 22, '-')
  print(res)

 


 

·编写测试用例

unittest中的测试用例定义的规范:

·定义一个测试用例类,测试用例类要继承unittest.TestCase

·测试用例类中,测试方法要以test_开头,就是一条测试用例

 

⭐继承了unittest.TestCase 会有标识;

 

⭐test_开头的方法也会有标识

 

 

 

 编写测试用例示范代码如下:

 

demo1_testcase.py
# 在写代码之前记得要引入unittest模块以及被测试的函数counter
# 单个用例文件只能单独运行整个模块或类,或单独运行某条用例
import unittest
from myCode import counter

# 定义一个测试用例类
class TestDemo(unittest.TestCase):

  def test_01add(self):
    # 第一步:准备用例数据
    # 用例的入参
    params = {'a': 11, 'b': 22, 'method': '+'}
    # 预期结果
    expected = 33
    # 第二步:调用功能函数(调用接口),获取实际结果
    result = counter(**params)
    # 第三步:比对预期结果和实际结果是否一致(python内置关键字assert断言
    # assert expected == result
    # unittest封装的断言方法,断言更详细
    self.assertEqual(expected, result)


  def test_02(self):
    # assert 11 == 22
    # 第一步:准备数据
    # 用例的入参
    params = {'a': 11, 'b': 22, 'method': '-'}
    # 预期结果
    expected = -11
    # 第二步:调用功能函数,获取实际结果
    result = counter(**params)
    # 第三步:断言
    self.assertEqual(expected, result)

  def test_03(self):
    # self.assertEqual(11, 22)
    # 第一步:准备数据
    # 用例的入参
    params = {'a': 2, 'b': 10, 'method': '*'}
    # 预期结果
    expected = 20
    # 第二步:调用功能函数,获取实际结果
    result = counter(**params)
    # 第三步:断言
    self.assertEqual(expected, result)
  def test_04(self):
    # 第一步:准备数据
    # 用例的入参
    params = {'a': 20, 'b': 10, 'method': '/'}
    # 预期结果
    expected = 2
    # 第二步:调用功能函数,获取实际结果
    result = counter(**params)
    # 第三步:断言
    self.assertEqual(expected, result)
  def test_05(self):
    # 第一步:准备数据
    # 用例的入参
    params = {'a': 11, 'b': 22, 'method': 'a'}
    # 预期结果
    expected = '计算方式有误'
    # 第二步:调用功能函数,获取实际结果
    result = counter(**params)
    # 第三步:断言
    self.assertEqual(expected, result)
  def test_06(self):
    # 第一步:准备数据
    # 用例的入参
    params = {'a': 11.3, 'b': 99, 'method': '+'}
    # 预期结果
    expected = '参数a和b只能为整数类型的数据'
    # 第二步:调用功能函数,获取实际结果
    result = counter(**params)
    # 第三步:断言
    self.assertEqual(expected, result)

 

# 编写测试用例在main函数下面
if __name__ == '__main__'
  unittest.main()


·生成测试集

如上是利用unittest.main()执行所有的用例,但是有的时候是不需要执行所有的用例的,可以按照自己的需求去添加用例。那就要使用TestSuite来帮助我们更好的实现单元测试了。

测试套件:unittest.TestSuite

用例加载器:unittest.TestLoader

demo2_testsuite.py

 

1-用addTest增加一条用例:测试类类名(测试方法名)
# 用addTest增加一条用例:测试类类名(测试方法名)
import unittest
# 一、创建一个套件
suite = unittest.TestSuite()

# 二、增加一条用例,先加哪个套件,先执行
from work15.demo1_testcase import TestDome
suite.addTest(TestDome('test_01add'))

addTest是其中的一种加载测试用例的方式,还有一种是通过TestLoadder来加载测试用例,如下三种方式:

 

2-通过测试类来加载用例:load.loadTestsFromTestCase
# 添加测试类到测试套件 load.loadTestsFromTestCase
import unittest
# 一、创建一个套件
suite = unittest.TestSuite()
# print(suite) 测试套件里面还没有用例
# <unittest.suite.TestSuite tests=[]>

# 二、创建一个用例加载器
load = unittest.TestLoader()

# 三、加载用例到测试套件
# 3.1添加测试类到测试套件
from work15.demo1_testcase import TestDome
suite.addTest(load.loadTestsFromTestCase(TestDome))
# print(suite) 打印出测试类里面的所有用例



3-通过测试所在模块来加载用例-load.loadTestsFromModule
# 添加测试模块到测试套件 load.loadTestsFromModule
import unittest

# 一、创建一个套件
suite = unittest.TestSuite()

# 二、创建一个用例加载器
load = unittest.TestLoader()

# 三、加载用例到测试套件
# 3.2添加测试模块到测试套件
from work15 import demo1_testcase
suite.addTest(load.loadTestsFromModule(demo1_testcase))


4-添加一个测试的目录load.discover
# 添加一个测试的目录 load.discover
# 测试用例目录下面的用例模块必须要使用test开头(也可以通过参数pattern指定查找用例模块的规则,pattern='test*.py')
import unittest

# 一、创建一个套件
suite = unittest.TestSuite()

# 二、创建一个用例加载器
load = unittest.TestLoader()

# 三、加载用例到测试套件
# 3.3、添加一个测试的目录
suite.addTest(load.discover(r'D:\autotest\pythonn-api-test0\work15\testcase'))


5-测试中使用
import unittest

# 以上三个步骤可以简写为一行(实际使用):
suite = unittest.defaultTestLoader.discover(r'D:\autotest\pythonn-api-test0\work15\testcase')

print(suite)

 

 

·执行测试用例

上面已经生成测试集,加载测试用例,接下来就需要执行测试集中的用例,会用到TextTestRunner模块,生成一个用例“执行器”专门来执行测试用例

 

测试运行程序:unittest.TextTestRunner

此为单独创建一个run.py文件

run.py
import unittest
# 方法一:将测试套件文件导入
from work15 import demo2_testsuite.py
# 创建一个测试运行程序
runner = unittest.TextTestRunner()
runner.run(demo2_testsuite.suite)


# 方法二:创建一个测试套件,加载测试用例到测试套件

# 不单独写测试集模块方法如下:
import unittest
# 方法二:加载测试用例到测试套件
suite = unittest.defaultTestLoader.discover(r'D:\autotest\pythonn-api-test0\work15\testcase')
# 创建一个测试运行程序
runner = unittest.TextTestRunner()
runner.run(suite)

 

 执行结果:

 有失败或者错误的结果:

 

 

·生成测试报告

①文本格式的测试报告生成1

 报告打开结果:

 

②文本格式的测试报告生成2

最后要生成测试报告,TextTestRunner是能够直接存储测试结果的,部分源码如下所示(暂不写):

import unittest

# 加载测试用例到测试套件
suite = unittest.defaultTestLoader.discover(r'D:\autotest\pythonn-api-test0\work15\testcase')

# 文本格式的测试报告生产(unittest自带的文本测试报告)
with open('report.txt','w',encoding='utf-8') as f:
# 通过类创建runner对象
runner = unittest.TextTestRunner(stream=f,'test',2)
# runner对象调用他的run方法
runner.run(suite)


TextTestRunner会把TestTestResult存储的结果存到指定的文件中去,TextTestRunner里面的方法有三个参数,("存储结果的文件","测试报告描述","存储模式")
存储模式有0,1,2三种模式。设置为2那么所有的测试结果会存储到当前目录下的test.txt中,执行代码后的测试结果为:

 

③使用第三方的扩展库来生成HTML格式的测试报告
# ---------------使用第三方的扩展库来生产测试报告---------------
# 1、安装命令:pip install BeautifulReport
import unittest

suite = unittest.defaultTestLoader.discover(r'C:\project\py41_class\py41_day15\testcase')

from BeautifulReport import BeautifulReport

br = BeautifulReport(suite)
br.report(description='花园生成的报告', filename='report.html', report_dir='./reports')
# 不指定路径,就默认生成在当前运行文件的同级目录下

 

 执行结果:

 

④unittestreport报告

 # 可以加镜像源地址下载

# 不要直接在settings里面下,settings里面下载是下到pycharm里面的,脱离了pycharm用不了,

后面用Jenkins跑后,如果包全部在pycharm的settings里面下的,用Jenkins跑的话包就加载不出来

 

# ===================2unittestreport===================
import unittest

# 安装命令:pip install unittestreport
# 官方文档:https://unittestreport.readthedocs.io/en/latest/#
suite = unittest.defaultTestLoader.discover(r'D:\autotest\pythonn-api-test0\work15\testcase')

from unittestreport import TestRunner

runner = TestRunner(suite,
filename="musen.html",
report_dir="./reports",
title='python自动化研究院生成的报告',
tester='小柠檬',
desc="500万的大项目测试报告",
templates=2
)
runner.run()

 执行结果:

 

 

·fixture

demo4_fixture.py

"""
测试夹具(测试用例的前后置方法):

setUp:用例级别的前置方法,每条用例执行之前都会执行
setUpClass:测试类级别的前置方法,每个测试类中的用例执行之前都会执行(只会执行一次)
tearDown:用例级别的后置方法,每条用例执行完之后都会执行
tearDownClass:测试类级别的后置方法,每个测试类中的用例全部执行完之后会执行(只会执行一次)
可以做脏数据的处理
"""

 问:跑自动化时,执行过程中产生的脏数据如何清理?

# 整个测试用例跑完后,数据用不到了,可以在后置方法里面执行SQL语句删除(如果有数据库执行权限,如无,则脏数据不用你管)

 

1.3)练习

接口自动化框架演变

测试登录的功能

①登录功能函数

def login_check(username=None, password=None):
  """
  登录校验的函数
  :param username: 账号
  :param password: 密码
  :return: dict type
  """
  if username != None and password != None:
    if username == 'python35' and password == 'lemonban':
      return {"code": 0, "msg": "登录成功"}
    else:
      return {"code": 1, "msg": "账号或密码不正确"}
  else:
    return {"code": 1, "msg": "所有的参数不能为空"}

if __name__ == '__main__':
  # 1、账号密码正确
  res = login_check('python35', 'lemonban')
  expected = {"code": 0, "msg": "登录成功"}
  # if res == expected:
    # print("用例:正确的账号密码 --执行通过")
  assert res == expected
  # 2、密码错误
  res = login_check('python35', 'lemonban222')
  expected = {"code": 1, "msg": "账号或密码不正确"}
  assert res == expected
  # if res == expected:
    # print("用例:密码错误 --执行通过")

 



②编写测试用例


import unittest

from work16.work15.myCode import login_check

class TestLogin(unittest.TestCase):

  def test_login_pass(self):

    """正向用例:登录成功"""
    # 第一步:准备用例数据
    params = {'username': "python35", "password": "lemonban"}
    expected = {"code": 0, "msg": "登录成功"}
    # 第二步:调用被测的功能函数(发送请求调用接口),获取实际结果
    result = login_check(**params)
    # 第三步:断言
    self.assertEqual(expected, result)
   
  def test_login_pwd_error(self):
    """反向用例:密码错误"""
    #第一步:准备用例数据
    params = {'username': "python35", "password": "lemonban11"}
    expected = {"code": 1, "msg": "账号或密码不正确"}
    # 第二步:调用被测的功能函数(发送请求调用接口),获取实际结果
    result = login_check(**params)
    # 第三步:断言
    self.assertEqual(expected, result)

  def test_login_user_error(self):
    """反向用例:账号错误"""
    # 第一步:准备用例数据
    params = {'username': "python3522", "password": "lemonban"}
    expected = {"code": 1, "msg": "账号或密码不正确"}
    # 第二步:调用被测的功能函数(发送请求调用接口),获取实际结果
    result = login_check(**params)
    # 第三步:断言
    self.assertEqual(expected, result)

  def test_login_user_and_pwd_error(self):
    """反向用例:账号密码都错误"""
    # 第一步:准备用例数据
    params = {'username': "python3522", "password": "lemonban111"}
    expected = {"code": 1, "msg": "账号或密码不正确"}
    # 第二步:调用被测的功能函数(发送请求调用接口),获取实际结果
    result = login_check(**params) # (username="python3522",password="lemonban111")
    # 第三步:断言
    self.assertEqual(expected, result)

  def test_login_user_is_none(self):
    """反向用例:账号为空"""
    # 第一步:准备用例数据
    param = {"password": "lemonban"}
    expected = {"code": 1, "msg": "所有的参数不能为空"}
    # 第二步:调用被测的功能函数(发送请求调用接口),获取实际结果
    result = login_check(**params)
    # 第三步:断言
    self.assertEqual(expected, result)

  def test_login_pwd_is_none(self):
    """反向用例:密码为空"""
    # 第一步:准备用例数据
    params = {'username': 'python35'}
    expected = {"code": 1, "msg": "所有的参数不能为空"}
    # 第二步:调用被测的功能函数(发送请求调用接口),获取实际结果
    result = login_check(**params)
    # 第三步:断言
    self.assertEqual(expected, result)

 

class TestLogin2(unittest.TestCase):

  def test_login_pass(self):

    """正向用例:登录成功"""
    # 第一步:准备用例数据
    params = {'username': "python35", "password": "lemonban"}
    expected = {"code": 0, "msg": "登录成功"}
    # 第二步:调用被测的功能函数(发送请求调用接口),获取实际结果
    result = login_check(**params)
    # 第三步:断言
    self.assertEqual(expected, result)
   
  def test_login_pwd_error(self):
    """反向用例:密码错误"""
    #第一步:准备用例数据
    params = {'username': "python35", "password": "lemonban11"}
    expected = {"code": 1, "msg": "账号或密码不正确"}
    # 第二步:调用被测的功能函数(发送请求调用接口),获取实际结果
    result = login_check(**params)
    # 第三步:断言
    self.assertEqual(expected, result)

  def test_login_user_error(self):
    """反向用例:账号错误"""
    # 第一步:准备用例数据
    params = {'username': "python3522", "password": "lemonban"}
    expected = {"code": 1, "msg": "账号或密码不正确"}
    # 第二步:调用被测的功能函数(发送请求调用接口),获取实际结果
    result = login_check(**params)
    # 第三步:断言
    self.assertEqual(expected, result)

  def test_login_user_and_pwd_error(self):
    """反向用例:账号密码都错误"""
    # 第一步:准备用例数据
    params = {'username': "python3522", "password": "lemonban111"}
    expected = {"code": 1, "msg": "账号或密码不正确"}
    # 第二步:调用被测的功能函数(发送请求调用接口),获取实际结果
    result = login_check(**params) # (username="python3522",password="lemonban111")
    # 第三步:断言
    self.assertEqual(expected, result)

  def test_login_user_is_none(self):
    """反向用例:账号为空"""
    # 第一步:准备用例数据
    param = {"password": "lemonban"}
    expected = {"code": 1, "msg": "所有的参数不能为空"}
    # 第二步:调用被测的功能函数(发送请求调用接口),获取实际结果
    result = login_check(**params)
    # 第三步:断言
    self.assertEqual(expected, result)

  def test_login_pwd_is_none(self):
    """反向用例:密码为空""" # 用例方法的文档字符串注释会当成用例的描述,测试报告里面用例描述就是这个
    # 第一步:准备用例数据
    params = {'username': 'python35'}
    expected = {"code": 1, "msg": "所有的参数不能为空"}
    # 第二步:调用被测的功能函数(发送请求调用接口),获取实际结果
    result = login_check(**params)
    # 第三步:断言
    self.assertEqual(expected, result)


if __name__ == '__main__':
  unittest.main()

 



③执行测试用例并生成报告run.py

import unittest
from unittestreport import TestRunner
suite = unittest.defaultTestLoader.discover(r'D:\autotest\pythonn-api-test0\work16\work15\testcases')

runner = TestRunner(suite,
            filename="work15.html",
            report_dir="./reports",
            title='python自动化大数据研究院生成的报告',
            tester='小柠檬',
            desc="大数据测试报告",
            templates=1
            )
runner.run()

 

 

 

2.DDT数据驱动

 

2.1)unittest中常用的断言方法

 

方法检查
assertEqual(a,b) a==b
assertNotEqual(a,b) a!=b
assertTrue(x) bool(x) is True
assertFalse(x) bool(x) is False
assertIs(a,b) a is b
assertIsNOT(a,b) a is notb
assertIsNone(x) x is None
assertIsNotNone(x) x is not None
assertIn(a,b) a in b
assertNotIn(a,b) a not in b
assertIsInstance(a,b) isinstance(a,b)
assertIsNotInstance(a,b) not isinstance(a,b)
assertGreater(a,b) a>b
assertGreaterEqual(a,b) a>=b
assertLess(a,b) a<b
assertLessEqual(a,b) a<=b

 # python中非0为True

 # 0 0.0 0+0j "" [] {} () False 为False

 # 什么地方用?web自动化-点击后是否跳转到对应的页面(找到某个东西则说明进入到了某个页面)

 

import unittest

class My(unittest.TestCase):
pass


#点击
#TestCase,可以查看到所有的unittest里面的断言方法

 

 

2.2)用例数据参数化之ddt

import unittest
from ddt import ddt, data

@ddt
class TestDemo(unittest.TestCase):
# 以测试数据为驱动,生成测试用例,data里面有多少条数据就生成多少条测试用例
# 每一条数据,执行用例时会当成参数传给item接收
@data(1, 22, 3, 4, 5, 6, 7)
def test_login(self, item):
print('item:', item)

 

 

 

 

 

 

2.3)将练习中的测试用例ddt参数化

test_demo1.py

import unittest
from work16.work15.myCode import login_check
from ddt import ddt, data

cases = [
{'params': {'username': "python35", "password": "lemonban"},
'expected': {"code": 0, "msg": "登录成功"}
},
{'params': {'username': "python35", "password": "lemonban11"},
'expected': {"code": 1, "msg": "账号或密码不正确"}
},
{'params': {'username': "python3522", "password": "lemonban"},
'expected': {"code": 1, "msg": "账号或密码不正确"}
},
{'params': {'username': "python3522", "password": "lemonban111"},
'expected': {"code": 1, "msg": "账号或密码不正确"}
},
]

@ddt
class TestLogin(unittest.TestCase):

@data(*cases)
def test_login_pass(self, item):
# 删除备注,因为用例描述信息每一条用例都会一样
print('item:', item) # 通过print输出的内容会在测试报告里面显示出来
# 第一步:准备用例数据
# 解包后参数是字典,所以可以通过key取值
params = item['params']
expected = item['expected']
# 第二步:调用被测的功能函数(发送请求调用接口),获取实际结果
result = login_check(**params) # 值也是字典,**解包
# 第三步:断言
self.assertEqual(expected, result)

 

 run.py

import unittest
from unittestreport import TestRunner


suite = unittest.defaultTestLoader.discover(r'D:\autotest\pythonn-api-test0\work16\testcases')

# 不传位置,默认在当前目录下生成report.html测试报告
runner = TestRunner(suite)

runner.run()

 

 

2.4)利用unittestreport模块里面的ddt实现用例描述添加

 

 用例数据是列表(元组)嵌套字典的格式,title字段可以在测试报告里面添加描述

 

2.4.1 list_data

import unittest
from work16.work15.myCode import login_check
from unittestreport import ddt, list_data

# title会显示在测试报告的用例描述里
cases = [
{'params': {'username': "python35", "password": "lemonban"},
'expected': {"code": 0, "msg": "登录成功"},
'title': "正向用例:登录成功"
},
{'params': {'username': "python35", "password": "lemonban11"},
'expected': {"code": 1, "msg": "账号或密码不正确"},
'title': "反向用例:密码错误"
},
{'params': {'username': "python3522", "password": "lemonban"},
'expected': {"code": 1, "msg": "账号或密码不正确"},
'title': "反向用例:账号错误"
},
{'params': {'username': "python3522", "password": "lemonban111"},
'expected': {"code": 1, "msg": "账号或密码不正确"},
'title': "反向用例:账号密码都错误"
},
]

@ddt
class TestLogin2(unittest.TestCase):

# 不需要解包
@list_data(cases)
def test_login_pass(self, item):
print('item:', item)
# 第一步:准备用例数据
params = item['params']
expected = item['expected']
# 第二步:调用被测的功能函数(发送请求调用接口),获取实际结果
result = login_check(**params)
# 第三步:断言
self.assertEqual(expected, result)

 

 

 

2.4.2 json_data

①单独创建一个json文件,放到data文件夹里:login.json

②利用json_data编写测试用例

login.json
[
{"params": {"username": "python35", "password": "lemonban"},
"expected": {"code": 0, "msg": "登录成功"},
"title": "正向用例:登录成功"
},
{"params": {"username": "python35", "password": "lemonban11"},
"expected": {"code": 1, "msg": "账号或密码不正确"},
"title": "反向用例:密码错误"
},
{"params": {"username": "python3522", "password": "lemonban"},
"expected": {"code": 1, "msg": "账号或密码不正确"},
"title": "反向用例:账号错误"
},
{"params": {"username": "python3522", "password": "lemonban111"},
"expected": {"code": 1, "msg": "账号或密码不正确"},
"title": "反向用例:账号密码都错误"
}
]

 

 test_demo.py
import unittest
from work16.work15.myCode import login_check
from unittestreport import ddt, json_data

"""
json格式数据和python数据的不同点:
python中的列表 :[] ---->在json中叫做数组 Array
python中的字典 :{key:value} ---->在json中叫做对象 object
python中的布尔值:True False ---->在json中的布尔值 true false
python中的空值:None ---->在json中的布尔值 null
json中的引号 统一使用双引号

关于使用unittestreport中json文件数据做参数化处理:
第一步:from unittestreport import ddt, json_data
第二步:在测试类上面@ddt
第三步:在测试方法上面@json_data,传入json数据文件的路径
第四步:在测试方法中定义一个参数,用来接收json_data传递过来的用例数据

注意点:json文件中的数据 要是数组(列表)嵌套对象(字典)的格式

"""


@ddt
class TestLoginJson(unittest.TestCase):

# 传入json文件的用例数据
@json_data(r'D:\autotest\pythonn-api-test0\work16\data\login.json')
def test_login_json(self, item):
print(item) # 用例描述
# 第一步:准备用例数据
params = item['params']
expected = item['expected']
# 第二步:调用被测的功能函数(发送请求调用接口),获取实际结果
result = login_check(**params)
# 第三步:断言
self.assertEqual(expected, result)

 

 

 

·python数据转换为json

json数据和python中数据类型的转换
1、json.dumps:将python中的数据转换为 json数据
2、json.loads:将json数据转换为python中的数据
3、json.load:加载json文件,转换为python对应的数据格式
4、json.dump:将python数据,写入json文件(自动化转换格式)

 

 

⭐ crtl+R 在login.json文档里可以进行字符替换

 

·将json数据转换为python数据
json.loads:将json数据转换为python中的数据

 

·加载json文件,转换为python对应的数据格式
json.load:加载json文件,转换为python对应的数据格式
test.json

 

 

 

 

·将python数据,写入json文件(自动转换格式)

运行完后在指定路径产生data.json文件

 

 

 

 

2.4.3 yaml_data

⭐没讲,看官网

 

yaml文件格式:

-   表示列表

键值对开头表示字典

:后面有个空格

 pip install pyyaml

 

·yaml文件的读取
data.yaml
- 11
- 22
- 33
- 44
- age: '18'
name: musen

 data2.yaml
name: musen
age: 18
lists:
- 11
- 22
- 33
- 44
- 55
cases:
params: 111
expected: 22

 

 # 把文本格式的数据转换成对应的python数据(列表或字典)

 

 

·将python数据写入yaml文件
import yaml

cases = [
{"excepted": {"code": 1, "msg": "注册成功"},
"params": ['python3', '123456', '123456'],
"title": "注册成功"
},
{"excepted": {"code": 0, "msg": "两次密码不一致"},
"params": ['python4', '1234567', '123456'],
"title": "两次密码不一致"
},
{"excepted": {"code": 0, "msg": "该账户已存在"},
"params": ['python31', '123456', '123456'],
"title": "账号已注册"
},
{"excepted": {"code": 0, "msg": "账号和密码必须在6-18位之间"},
"params": ['python1', '12345', '12345'],
"title": "密码长度少于6位"
},
{"excepted": {"code": 0, "msg": "账号和密码必须在6-18位之间"},
"params": ['python21', '1234561234561234567', '1234561234561234567'],
"title": "密码长度大于18位"
},
{"excepted": {"code": 0, "msg": "账号和密码必须在6-18位之间"},
"params": ['py012', '1234567', '1234567'],
"title": "账号长度少于6位"
}
]

with open('musen.yaml', 'w', encoding='utf-8') as f:
yaml.dump(cases, f, allow_unicode=True)

 

 

2.4.4 练习:用例数据与测试代码分离

①要测试的代码:myCode.py

②测试用例:testcase文件下以test_开头的py文件

③测试数据:data文件下的用例数据文件

④run.py

 

①myCode.py

##

# 
users = [{'user': 'python31', 'password': '123456'}]

def register(username=None, password1=None, password2=None):
# 判断是否有参数为空
if not all([username, password1, password2]):
return {"code": 0, "msg": "所有参数不能为空"}
# 注册功能
for user in users: # 遍历出所有账号,判断账号是否存在
if username == user['user']:
# 账号存在
return {"code": 0, "msg": "该账户已存在"}
else:
if password1 != password2:
# 两次密码不一致
return {"code": 0, "msg": "两次密码不一致"}
else:
# 账号不存在 密码不重复,判断账号密码长度是否在 6-18位之间
if 6 <= len(username) <= 18 and 6 <= len(password1) <= 18:
# 注册账号
users.append({'user': username, 'password': password2})
return {"code": 1, "msg": "注册成功"}
else:
# 账号密码长度不对,注册失败
return {"code": 0, "msg": "账号和密码必须在6-18位之间"}

 

 

②test_demo.py

import unittest
from work17.work16.myCode import register
from unittestreport import ddt, list_data

cases = [
{"excepted": {"code": 1, "msg": "注册成功"},
"params": ['python3', '123456', '123456'],
"title": "注册成功"
},
{"excepted": {"code": 0, "msg": "两次密码不一致"},
"params": ['python4', '1234567', '123456'],
"title": "两次密码不一致"
},
{"excepted": {"code": 0, "msg": "该账户已存在"},
"params": ['python31', '123456', '123456'],
"title": "账号已注册"
},
{"excepted": {"code": 0, "msg": "账号和密码必须在6-18位之间"},
"params": ['python1', '12345', '12345'],
"title": "密码长度少于6位"
},
{"excepted": {"code": 0, "msg": "账号和密码必须在6-18位之间"},
"params": ['python21', '1234561234561234567', '1234561234561234567'],
"title": "密码长度大于18位"
},
{"excepted": {"code": 0, "msg": "账号和密码必须在6-18位之间"},
"params": ['py012', '1234567', '1234567'],
"title": "账号长度少于6位"
}
]


@ddt
class RegisterTestCase(unittest.TestCase):

@list_data(cases)
def test_register(self, item):
"""正常注册"""
# 第一步:准备用例的数据
# 预期结果:
excepted = item['excepted']
# 参数:data
data = item['params']
# 第二步:调用被测试的功能函数,传入参数,获取实际结果:
# 对列表拆包,用一个*
res = register(*data)
# 第三步:断言(比对预期结果和实际结果)
self.assertEqual(excepted, res)



③run.py
# unittest调用默认加载器defaultTestLoader,加载用例
import unittest
from unittestreport import TestRunner

# 相对路径,传入当前目录下的testcase目录,加载用例到测试套件
# 运行文件run.py和测试目录在同一级别用.
suite = unittest.defaultTestLoader.discover(r".\testcase")
# 创建一个用例运行器,传入测试套件
runner = TestRunner(suite)
# 调用运行器的run方法执行用例
runner.run()





3.python操作excel

pip install openpyxl

只能执行xlsx的excel文件

 

excel中的三大对象:

 

3.1)excel文件读写

import openpyxl

# 1、加载excel文件为一个工作簿对象(workbook)
wb = openpyxl.load_workbook(filename=r'D:\autotest\pythonn-api-test0\work17\data\data.xlsx')
# print(wb) # ①是一个Workbook对象
# 2、选中要读取的表单
login_sh = wb['login'] # ②是一个表单对象
print(login_sh) # 打印表单对象 <Worksheet "login">
# 3、读取表格中的数据(通过指定行,列取获取格子对象)
res = login_sh.cell(row=1,column=1) # ③cell是一个表格对象
print(res) # 打印格子对象 ,选中第一行第一列这个格子<Cell 'login'.A1>
# 4、获取格子中的数据(格子对象的value属性)
data = res.value
print(data)
# 5、往表格中写入数据,只是写入到工作簿对象里面
login_sh.cell(row=3,column=2,value='java666')
# 6、将最新的工作薄对象保存为文件,保存之后写入才成功
wb.save(filename=r'D:\autotest\pythonn-api-test0\work17\data\data.xlsx')


3.2)批量读取表单中的数据

import openpyxl

# 1、加载文件为工作簿对象
wb = openpyxl.load_workbook(filename=r'D:\①testing\⑤autotest\api-test0\work17\data\data.xlsx')
# 2、选中表单
login_sh = wb['login']

# 3、按行获取表单所有的格子对象--获取出来的数据是列表嵌套元组的格式
rows = login_sh.rows
datas = list(rows)

"""
[
(<Cell 'login'.A1>, <Cell 'login'.B1>, <Cell 'login'.C1>),
(<Cell 'login'.A2>, <Cell 'login'.B2>, <Cell 'login'.C2>),
(<Cell 'login'.A3>, <Cell 'login'.B3>, <Cell 'login'.C3>)
]
"""
# # 遍历读取出来的所有数据
# for item in datas:
# # 遍历出来的item 是每行的所有格子对象
# print(item)
# for c in item:
# # c 是一个格子对象
# print(c.value)

# -----------------需求,将所有的数据读取出来组装为列表嵌套列表的格式------------
new_list = []
# 4、遍历读取出来的数据
for item in datas:
li = []
# 遍历出来的item 是每行的所有格子对象
for c in item:
# c 是一个格子对象
li.append(c.value)
print('li:', li)
# 将这一行的数据添加到new_list中
new_list.append(li)
print(new_list)




3.3)列表推导式

   

# 1、for循环往列表中添加数据
li = []
for i in range(5):
li.append('python{}'.format(i))
print(li)


# 2、列表推导式
li = ['python{}'.format(i) for i in range(5)]
print(li)

3.4)批量读取表单数据-优化版

 

import openpyxl

# 1、加载文件为工作簿对象
wb = openpyxl.load_workbook(filename=r'D:\autotest\pythonn-api-test0\work17\data\data.xlsx')
# 2、选中表单
login_sh = wb['login']

# 3、按行获取表单所有的格子对象--获取出来的数据时列表嵌套元组的格式
rows = login_sh.rows
datas = list(rows)

# 4、通过推导式的语法读取表单中的数据
new_list = []
# 遍历读取出来的数据
for item in datas:
li = [c.value for c in item]
new_list.append(li)
print(new_list)

# new_list = [[c.value for c in item] for item in datas]
# print(new_list)

3.5)读取excel中的用例数据

 

import openpyxl

# 1、加载文件为工作簿对象
wb = openpyxl.load_workbook(filename=r'D:\autotest\pythonn-api-test0\work17\data\data.xlsx')
# 2、选中表单
login_sh = wb['register']

# 3、按行获取表单所有的格子对象--获取出来的数据时列表嵌套元组的格式
rows = login_sh.rows
datas = list(rows)

# 4、通过推导式的语法读取表单中的数据
new_list = []
# 遍历读取出来的数据
for item in datas:
li = [c.value for c in item]
new_list.append(li)

# print(new_list)
"""
[
['case_id', 'title', 'params', 'expected'],
[1, '注册成功', "['python3', '123456', '123456']", '{"code": 1, "msg": "注册成功"}'],
[2, '两次密码不一致', "['python4', '1234567', '123456']", '{"code": 0, "msg": "两次密码不一致"}'],
[3, '账号已注册', "['python31', '123456', '123456']", '{"code": 0, "msg": "该账户已存在"}'],
[4, '密码长度少于6位', "['python1', '12345', '12345']", '{"code": 0, "msg": "账号和密码必须在6-18位之间"}']
]
"""
title = new_list[0]
# print('title:', title)
cases =[]
for i in new_list[1:]:
res = dict(zip(title, i))
cases.append(res)
print(cases)

 

 

3.6)读取excel中的用例数据-优化版

一个循环实现:

import openpyxl

# 1、加载文件为工作簿对象
wb = openpyxl.load_workbook(filename=r'D:\autotest\pythonn-api-test0\work17\data\data.xlsx')
# 2、选中表单
login_sh = wb['register']

# 3、按行获取表单所有的格子对象--获取出来的数据时列表嵌套元组的格式
rows = login_sh.rows
datas = list(rows)

# 4、通过推导式的语法读取表单中的数据
new_list = []
# 5、获取表头的数据
title = [i.value for i in datas[0]]
# 6、遍历表头以外的其他行数据
for item in datas[1:]:
# 获取遍历出来的行中所有的数据
li = [c.value for c in item]
# 和表头打包为字典
d = dict(zip(title,li))
# 将打包的字典,添加搭配new_list中
new_list.append(d)
print(new_list)

# 可以用eval去掉params值的引号





3.7)封装excel读取数据类

excel文档注意点:不要在用例表格里面的其他地方填写空格,不然会导致读取出许多空行或空列的数据。

"""
封装的需求:
1、方便读取excel中的数据

封装的方法:
1、封装一个读数据的方法
入参:
读取的文件(文件路径)
读取的表单(表单名)
返回值:
读取出来组装好的数据

2、写入数据的方法
入参:文件,表单,行,列,写入的值
"""
import openpyxl


class ExcelHandle:

def read_data(self, filepath, sheet):
"""
读取excel数据的方法
:param filepath: 读取的文件(文件路径)
:param sheet: 读取的表单(表单名)
:return:
"""
# 1、加载文件为工作簿对象
wb = openpyxl.load_workbook(filepath)
# 2、选中表单
sh = wb[sheet]
# 3、按行获取所有的格子对象
rows_list = list(sh.rows)
# 4、获取表头
title = [i.value for i in rows_list[0]]
cases = []
# 5、遍历表头外的其他行
for item in rows_list[1:]:
# 获取遍历出来这一行的所有数据,放到列表中
li = [i.value for i in item]
# 将表头和这一行的数据打包,转换为字典
res = dict(zip(title, li))
cases.append(res)
return cases

def write_data(self, filepath, sheet, row, column, value):
"""
:param filepath: 要写入的excel文件路径
:param sheet: 表单名
:param row: 行
:param column: 列
:param value: 写入的值
:return:
"""
# 1、加载excel文件为一个工作薄对象
wb = openpyxl.load_workbook(filepath)
# 2、选取要操作的表单
sh = wb[sheet]
# 3、写入值
# sh.cell(row=row, column=column, value=value)
sh.cell(row, column).value = value
# 4、将工作薄保存为文件
wb.save(filepath)


if __name__ == '__main__':
excel = ExcelHandle()
data = excel.read_data(r'D:\autotest\pythonn-api-test0\work18\data\data.xlsx', 'register')
# print(data)
file = r'D:\autotest\pythonn-api-test0\work18\data\data.xlsx'
# excel.write_data(file, 'Sheet2', 1, 1, 'python')
excel.write_data(file, 'login', 3, 4, '自动化测试')


3.8)封装excel读取数据类-优化版

运行写操作,要关闭excel文件

 

 

# 文件名在创建对象的时候就传进入,通过实例属性保存起来

"""
封装的需求:
1、方便读取excel中的数据

封装的方法:
1、封装一个读数据的方法
入参:
读取的文件(文件路径)
读取的表单(表单名)
返回值:
读取出来组装好的数据

2、写入数据的方法
入参:文件,表单,行,列,写入的值

"""
import openpyxl


class ExcelHandle:

def __init__(self, filepath):
"""
:param filepath: 要操作的文件
"""
self.file = filepath

def read_data(self, sheet):
"""
读取excel数据的方法
:param sheet: 读取的表单(表单名)
:return:
"""
# 1、加载文件为工作簿对象
wb = openpyxl.load_workbook(self.file)
# 2、选中表单
sh = wb[sheet]
# 3、按行获取所有的格子对象
rows_list = list(sh.rows)
# 4、获取表头
title = [i.value for i in rows_list[0]]
cases = []
# 5、遍历表头外的其他行
for item in rows_list[1:]:
# 获取遍历出来这一行的所有数据,放到列表中
li = [i.value for i in item]
# 将表头和这一行的数据打包,转换为字典
res = dict(zip(title, li))
cases.append(res)
return cases

def write_data(self, sheet, row, column, value):
"""
:param sheet: 表单名
:param row: 行
:param column: 列
:param value: 写入的值
:return:
"""
# 1、加载excel文件为一个工作薄对象
wb = openpyxl.load_workbook(self.file)
# 2、选取要操作的表单
sh = wb[sheet]
# 3、写入值
# sh.cell(row=row, column=column, value=value)
sh.cell(row, column).value = value
# 4、将工作薄保存为文件
wb.save(self.file)


if __name__ == '__main__':
excel = ExcelHandle(r'D:\autotest\pythonn-api-test0\work18\data\data.xlsx')
data = excel.read_data('register')
data2 = excel.read_data('login')
excel.write_data('login', 4, 1, '木森')

3.9)用例与数据分离

debug查看数据类型

 

 

将字符串eval成对应类型的数据

 

# excel格子里面是纯数字读取出来就是数值类型,其他数据读取出来都是字符串

# 如果里面的数据比较复杂,比如又null true要进行转化的,就用loads

# 回写测试结果到excel

import unittest
from work18.demo2_excelhandle import ExcelHandle
from unittestreport import ddt, list_data
from work18.myCode import register

# 读取excel中的用例数据
excel = ExcelHandle(r'D:\autotest\pythonn-api-test0\work18\data\data.xlsx')
cases = excel.read_data('register')

""" 通过用例编号获取行号 """
# @ddt
# class TestRegister(unittest.TestCase):
#
# @list_data(cases)
# def test_register(self, item):
# # 第一步:准备用例数据
# # 如果参数中有true null等使用json.loads
# params = eval(item['params'])
# expected = eval(item['expected'])
# case_id = item['case_id']
# # 第二步:调用对应de测试函数(请求接口),获取返回的实际结果
# result = register(*params)
# try:
# # 第三步:比对预期和实际结果是否一致(断言)
# self.assertEqual(expected, result)
# except AssertionError as e:
# excel.write_data('register', row=case_id + 1, column=5, value='执行不通过')
# print('不通过')
# # 捕获到断言异常之后,一定要使用raise抛出异常,不然unittest监测不到这是一条失败的用例
# raise e
# else:
# excel.write_data('register', row=case_id + 1, column=5, value='通过')
# print('通过')

""" 通过索引获取行号,索引从0开始,与行号相差2"""
@ddt
class TestRegister(unittest.TestCase):

@list_data(cases)
def test_register(self, item):
# 第一步:准备用例数据
params = eval(item['params'])
expected = eval(item['expected'])
# 获取行号,用列表的index方法
r = cases.index(item) + 2
# 第二步:调用对应点测试函数(请求接口),获取返回的实际结果
result = register(*params)
try:
# 第三步:比对预期和实际结果是否一致(断言)
self.assertEqual(expected, result)
except AssertionError as e:
excel.write_data('register', row=r, column=5, value='执行不通过')
# 捕获到断言异常之后,一定要使用raise抛出异常,不然unittest是监测不到这是一条失败的用例
raise e
else:
excel.write_data('register', row=r, column=5, value='通过')

 

4.配置文件的使用

ini、conf、cfg

 

配置块:section

配置项:option

 

 

 

配置文件解析器 https://docs.python.org/zh-cn/3/library/configparser.html

 

 

 

4.1)配置文件解析器

from configparser import ConfigParser

filepath = r'D:\autotest\pythonn-api-test0\work18\data\musem.ini'
# 创建一个配置文件解析器对象
conf = ConfigParser()
# 通过解析器去解析配置文件

conf.read(filepath, encoding='utf-8')
# 返回的是配置文件名称,可以不接收

# 读取配置文件中的配置
# 1、get方法:读取出来的数据是字符串
res = conf.get('mysql', 'port')
print(res)

 

# 2、getint :  # ----->int(get()) 只能读取数值
res = conf.getint('mysql', 'user')
print(res, type(res))
# 3、getfloat :  # ----->float(get())
res = conf.getfloat('mysql', 'port')
print(res, type(res))


# 4、getboolean: # ----->bool(get),只能读取bool值
res = conf.getboolean('mysql', 'status')
print(res,type(res))




4.2)配置文件的写入操作

 

# 了解即可

from configparser import ConfigParser

# 1、创建一个配置文件解析器对象
conf = ConfigParser()
# 2、通过解析器去解析配置文件
filepath = r'D:\autotest\pythonn-api-test0\work18\data\musem.ini'
conf.read(filepath, encoding='utf-8')

# 3、往配置文件中写入数据,section option value
conf.set('logging', 'name', 'p99999999999999')

# 4、将写入的内容保存到配置文件
with open(filepath, 'w', encoding='utf-8') as f:
conf.write(f)


4.3)配置文件常用方法

 

 

4.3.1)获取配置文件所有的配置块

from configparser import ConfigParser

# 创建一个配置文件解析器对象
conf = ConfigParser()
# 通过解析器去解析配置文件
filepath = r'D:\autotest\pythonn-api-test0\work18\data\musem.ini'
conf.read(filepath, encoding='utf-8')


# ①获取配置文件中所有的配置块
sections = conf.sections()
print(sections)
if 'db' in sections:
res = conf.get('db', 'port')
print(res)
else:
res = 'report.log'

4.3.2)获取指定的配置块下面所有的配置项

from configparser import ConfigParser

# 创建一个配置文件解析器对象
conf = ConfigParser()
# 通过解析器去解析配置文件
filepath = r'D:\autotest\pythonn-api-test0\work18\data\musem.ini'
conf.read(filepath, encoding='utf-8')

res = conf.options('mysql')
print(res) # 获取某配置块下所有的配置项

if 'db' in conf.sections() and 'port' in conf.options('db'):
res = conf.get('db', 'port')
print(res)
else:
res = 'report.log'

4.3.3)获取配置块下面所有的配置项和值

from configparser import ConfigParser

# 创建一个配置文件解析器对象
conf = ConfigParser()
# 通过解析器去解析配置文件
filepath = r'D:\autotest\pythonn-api-test0\work18\data\musem.ini'
conf.read(filepath, encoding='utf-8')

# 列表嵌套元组
res = conf.items('mysql')
print(res)
# 转换为字典
res = dict(conf.items('mysql'))
print(res)


 

 

4.4)配置文件操作的封装

from configparser import ConfigParser


class CfgParser(ConfigParser):
# 重写配置文件解释器对象ConfigParser里面的init方法
def __init__(self, file, encoding='utf-8'):
# 调用被重写的父类方法
super().__init__()
# 初始化时候就把配置文件对象传入进来,加载配置文件到配置文件解析器
# 因为只有read方法使用了file,所以不用单独保存file属性
# 如果封装了一个写入的方法,就可以将file属性保存
# 封装就是简化加载那一步(要加载再读)
self.read(file, encoding=encoding)


if __name__ == '__main__':
conf = CfgParser(r'D:\autotest\pythonn-api-test0\work18\data\musem.ini')
# 要解析哪个配置文件,直接传给conf就可以了;初始化后,就已经创建了一个配置文件解析器,可以直接读取配置文件内容,不用再调用read方法去加载了
res = conf.get('mysql', 'user')
print(res)



5.logging日志模块

 

 

 

 

5.1)logging模块的基本使用

 

 

5.2)创建日志收集器

 

import logging

# 日志收集器的创建:logging.getLogger(日志收集器名称)
http_log = logging.getLogger('HTTP')

# 设置日志收集的等级为DEBUG,还要设置日志输出的等级。。
http_log.setLevel('DEBUG')

http_log.debug('----------DEBUG----------')
http_log.info('----------info----------')
http_log.warning('----------warning----------')
http_log.error('----------error----------')
http_log.critical('----------critical----------')

 

# 自己创建的日志收集器没有设置日志输出格式:只输出日志内容

 

5.3)创建日志输出渠道:控制台

import logging

# 1、日志收集器的创建:logging.getLogger(日志收集器名称)
http_log = logging.getLogger('HTTP')

# 2、设置日志收集的等级为DEBUG
http_log.setLevel('DEBUG')

# 3、创建一个日志输出渠道(控制台)
sh = logging.StreamHandler()
sh.setLevel('DEBUG')
# 4、将日志输出渠道添加到日志收集器中
http_log.addHandler(sh)

http_log.debug('----------DEBUG-------------')
http_log.info('----------info-------------')
http_log.warning('----------warning-------------')
http_log.error('----------error-------------')
http_log.critical('----------critical-------------')

 

 

5.4)创建日志输出渠道:文件

import logging

# 1、日志收集器的创建:logging.getLogger(名称)
http_log = logging.getLogger('HTTP')

# 2、设置日志收集的等级为DEBUG
http_log.setLevel('DEBUG')

# 3、创建一个日志输出渠道(控制台)
sh = logging.StreamHandler()
sh.setLevel('DEBUG')
# 4、将日志输出渠道添加到日志收集器中
http_log.addHandler(sh)

# 5、创建一个日志输出渠道(文件)
# fh = logging.FileHandler('musen.log', encoding='utf-8')
fh = logging.FileHandler(r'D:\①testing\⑤autotest\api-test0\work19\logs\lemon.log', encoding='utf-8')
fh.setLevel('ERROR')
# 6、将日志输出渠道添加到日志收集器
http_log.addHandler(fh)

http_log.debug('----------DEBUG---人人----------')
http_log.info('----------info---哈哈----------')
http_log.warning('----------warning---啊啊----------')
http_log.error('----------error---嗷嗷的----------')
http_log.critical('----------critical---哎哎哎----------')

 

 

 

 # pycharm设置的文件默认是utf-8编码格式打开,创建日志输出渠道的时候没有设置输出到文件的编码格式,默认会是GBK

# 自动生成一个日志文件

 

 

5.5)设置日志输出的格式

 

 

import logging

# 1、日志收集器的创建:logging.getLogger(名称)
http_log = logging.getLogger('HTTP')

# 2、设置日志收集的等级为DEBUG
http_log.setLevel('DEBUG')

# 创建一个日志输出的格式
mat= logging.Formatter('%(asctime)s - [%(filename)s-->line:%(lineno)d] - %(levelname)s: %(message)s')

# 3、创建一个日志输出渠道(控制台)
sh = logging.StreamHandler()
sh.setLevel('DEBUG')
sh.setFormatter(mat)
# 4、将日志输出渠道添加到日志收集器中
http_log.addHandler(sh)

# 5、创建一个日志输出渠道(文件)
fh = logging.FileHandler(r'D:\autotest\api-test0\work19\logs\lemon.log', encoding='utf-8')
fh.setLevel('ERROR')
fh.setFormatter(mat)

# 6、将日志输出渠道添加到日志收集器
http_log.addHandler(fh)

http_log.debug('----------DEBUG---人人----------')
http_log.info('----------info---哈哈----------')
http_log.warning('----------warning---啊啊----------')
http_log.error('----------error---嗷嗷的----------')
http_log.critical('----------critical---哎哎哎----------')

 

 

 

# 可以查看Formatter源码看他的日志格式怎么设置

 

 

 

 

5.6)封装日志收集器

import logging

def create_logger(name, level, f_level, s_level, filepath):
"""
创建日志收集器
:param name: 日志收集器的名称
:param level: 收集器的收集等级
:param f_level: 输出到文件的等级
:param s_level: 输出到控制台的等级
:param filepath: 日志文件的名称
:return:
"""
# 1、日志收集器的创建:logging.getLogger(名称)
http_log = logging.getLogger(name)
# 2、设置日志收集的等级为DEBUG
http_log.setLevel(level)
# 创建一个日志输出的格式
mat = logging.Formatter('【%(name)s】 | [%(asctime)s] | %(levelname)s: %(message)s')
# 3、创建一个日志输出渠道(控制台)
sh = logging.StreamHandler()
sh.setLevel(s_level)
sh.setFormatter(mat)
# 4、将日志输出渠道添加到日志收集器中
http_log.addHandler(sh)
# 5、创建一个日志输出渠道(文件)
fh = logging.FileHandler(filepath, encoding='utf-8')
fh.setLevel(f_level)
fh.setFormatter(mat)
# 6、将日志输出渠道添加到日志收集器
http_log.addHandler(fh)

return http_log


log = create_logger(xxx)


if __name__ == '__main__':
# 封装的文件里面已经调用了create_logger函数,下面就不需要再调用一次了
  #   log = create_logger(name='MUSEN', level='DEBUG', f_level='DEBUG', s_level='DEBUG', filepath='./lemonban.log')
log.debug('这是DEBUG等级的信息')
log.info('这是INFO等级的信息')
log.warning('这是WARNING等级的信息')
log.error('这是ERROR等级的信息')
log.critical('这是CRITICAL等级的信息')



# 还未分离出参数时,未参数化时在别的地方调用日志函数log对象

 

 

⭐不在模块里面调用函数时,在测试用例中引用参数化后的日志

from demo7_logger import create_logger

log = create_logger(name='MUSEN', level='DEBUG', f_level='DEBUG', s_level='DEBUG', filepath='./lemonban.log')

log.debug('这是DEBUG等级的信息')
log.info('这是INFO等级的信息')
log.warning('这是WARNING等级的信息')
log.error('这是ERROR等级的信息')
log.critical('这是CRITICAL等级的信息')

 

5.7)日志轮转器

 

 

 

import logging
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler

# def create_logger(name, level, f_level, s_level, filepath):
# """
# 创建日志收集器(按文件大小进行轮转)
# :param name: 日志收集器的名称
# :param level: 收集器的收集等级
# :param f_level: 输出到文件的等级
# :param s_level: 输出到控制台的等级
# :param filepath: 日志文件的名称
# :return:
# """
# # 1、日志收集器的创建:logging.getLogger(名称)
# http_log = logging.getLogger(name)
# # 2、设置日志收集的等级为DEBUG
# http_log.setLevel(level)
# # 创建一个日志输出的格式
# mat = logging.Formatter('【%(name)s】 | [%(asctime)s] | %(levelname)s: %(message)s')
# # 3、创建一个日志输出渠道(控制台)
# sh = logging.StreamHandler()
# sh.setLevel(s_level)
# sh.setFormatter(mat)
# # 4、将日志输出渠道添加到日志收集器中
# http_log.addHandler(sh)
# # 5、创建一个日志输出渠道(输出到文件的轮转器)
# fh = RotatingFileHandler(filepath, maxBytes=1024*1024*10, backupCount=3, encoding='utf-8')
# fh.setLevel(f_level)
# fh.setFormatter(mat)
# # 6、将日志输出渠道添加到日志收集器
# http_log.addHandler(fh)

# return http_log

def create_logger(name, level, f_level, s_level, filepath):
"""
创建日志收集器(按时间进行轮转)
:param name: 日志收集器的名称
:param level: 收集器的收集等级
:param f_level: 输出到文件的等级
:param s_level: 输出到控制台的等级
:param filepath: 日志文件的名称
:return:
"""
# 1、日志收集器的创建:logging.getLogger(名称)
http_log = logging.getLogger(name)
# 2、设置日志收集的等级为DEBUG
http_log.setLevel(level)
# 创建一个日志输出的格式
mat = logging.Formatter('【%(name)s】 | [%(asctime)s] | %(levelname)s: %(message)s')
# 3、创建一个日志输出渠道(控制台)
sh = logging.StreamHandler()
sh.setLevel(s_level)
sh.setFormatter(mat)
# 4、将日志输出渠道添加到日志收集器中
http_log.addHandler(sh)
# 5、创建一个日志输出渠道(输出到文件的轮转器)
# 按时间轮转
fh = TimedRotatingFileHandler(filepath, encoding='utf-8', when='S', interval=2, backupCount=7)
fh.setLevel(f_level)
fh.setFormatter(mat)
# 6、将日志输出渠道添加到日志收集器
http_log.addHandler(fh)

return http_log

log = create_logger(name='MUSEN', level='DEBUG', f_level='DEBUG', s_level='DEBUG', filepath='./logs/lemonban.log')


if __name__ == '__main__':
import time
###
log.debug('这是DEBUG等级的信息')
time.sleep(1)
log.info('这是INFO等级的信息')
time.sleep(1)
log.warning('这是WARNING等级的信息')
time.sleep(1)
log.error('这是ERROR等级的信息')
time.sleep(1)
log.critical('这是CRITICAL等级的信息')



3.简单框架编写

3.1)框架模型分析

 

3.2)分层设计

 

 

3.3)数据驱动

 

 

 3.4)代码编写

1.common

handle_excel.py

"""
封装读取excel数据的方法
"""

import openpyxl

class ExcelHandle:

def __init__(self, filepath):
"""
:param filepath: 要操作的文件
"""
self.file = filepath

def read_data(self, sheet):
"""
读取excel数据的方法
:param sheet: 读取的表单(表单名)
:return:
"""
# 1、加载文件为工作簿对象
wb = openpyxl.load_workbook(self.file)
# 2、选中表单
sh = wb[sheet]
# 3、按行获取所有的格子对象
rows_list = list(sh.rows)
# 4、获取表头
title = [i.value for i in rows_list[0]]
cases = []
# 5、遍历表头外的其他行
for item in rows_list[1:]:
# 获取遍历出来这一行的所有数据,放到列表中
li = [i.value for i in item]
# 将表头和这一行的数据打包,转换为字典
res = dict(zip(title, li))
cases.append(res)
return cases

def write_data(self, sheet, row, column, value):
"""
:param sheet: 表单名
:param row: 行
:param column: 列
:param value: 写入的值
:return:
"""
# 1、加载excel文件为一个工作薄对象
wb = openpyxl.load_workbook(self.file)
# 2、选取要操作的表单
sh = wb[sheet]
# 3、写入值
# sh.cell(row=row, column=column, value=value)
sh.cell(row, column).value = value
# 4、将工作薄保存为文件
wb.save(self.file)

handle_log.py

import logging
from logging.handlers import TimedRotatingFileHandler
from conf.settings import LOG


def create_logger(name, level, f_level, s_level, filepath):
"""
创建日志收集器(按时间进行轮转)
:param name: 日志收集器的名称
:param level: 收集器的收集等级
:param f_level: 输出到文件的等级
:param s_level: 输出到控制台的等级
:param filepath: 日志文件的名称
:return:
"""
# 1、日志收集器的创建:logging.getLogger(名称)
http_log = logging.getLogger(name)
# 2、设置日志收集的等级为DEBUG
http_log.setLevel(level)
# 创建一个日志输出的格式
mat = logging.Formatter('【%(name)s】 | [%(asctime)s] | %(levelname)s: %(message)s')
# 3、创建一个日志输出渠道(控制台)
sh = logging.StreamHandler()
sh.setLevel(s_level)
sh.setFormatter(mat)
# 4、将日志输出渠道添加到日志收集器中
http_log.addHandler(sh)
# 5、创建一个日志输出渠道(输出到文件的轮转器)
fh = TimedRotatingFileHandler(filepath, encoding='utf-8', when='D', interval=1, backupCount=7)
fh.setLevel(f_level)
fh.setFormatter(mat)
# 6、将日志输出渠道添加到日志收集器
http_log.addHandler(fh)

return http_log


log = create_logger(**LOG)

2.conf

settings.py

# 日志输出的相关配置
LOG = {
'name': 'MUSEN',
'level': 'DEBUG',
'f_level': 'DEBUG',
's_level': 'ERROR',
'filepath': 'D:\\①testing\\⑤autotest\\api-test1\\logs\\lemonban.log'
}

3.datas

data.xlsx

 

4.logs

lemon.log

5.reports

report_dir='./reports'

6.testcases

test_register.py

import unittest
from common.handle_excel import ExcelHandle
from unittestreport import ddt, list_data
from myCode import register
from common.handle_log import log

excel = ExcelHandle(r'D:\①testing\⑤autotest\api-test1\datas\data.xlsx')
cases = excel.read_data('register')


@ddt
class TestRegister(unittest.TestCase):

@list_data(cases)
def test_register(self, item):
# 第一步:准备数据
params = eval(item['params'])
expected = eval(item['expected'])
# 第二步:调用功能函数(调用接口),获取实际结果
result = register(*params)
log.info('预期结果:{}'.format(expected))
log.info('实际结果:{}'.format(result))
# 第三:断言
try:
self.assertEqual(expected, result)
except AssertionError as e:
log.error('【{}】-- 用例执行失败'.format(item['title']))
       # 输出断言的异常信息
# log.error(e)
log.exception(e)
raise e
else:
log.info('【{}】--用例执行成功'.format(item['title']))

# ⭐用log.error(e)

# ⭐用log.exception(e)异常对象 更详细

 

 

7.run.py

 

import unittest
from unittestreport import TestRunner


suite = unittest.defaultTestLoader.discover(r'D:\①testing\⑤autotest\api-test1\testcases')

runner = TestRunner(suite,report_dir='./reports')

runner.run()


⭐被测函数myCode.py

users = [{'user': 'python31', 'password': '123456'}]

def register(username=None, password1=None, password2=None):
# 判断是否有参数为空
if not all([username, password1, password2]):
return {"code": 0, "msg": "所有参数不能为空"}
# 注册功能
for user in users: # 遍历出所有账号,判断账号是否存在
if username == user['user']:
# 账号存在
return {"code": 0, "msg": "该账户已存在"}
else:
if password1 != password2:
# 两次密码不一致
return {"code": 0, "msg": "两次密码不一致"}
else:
# 账号不存在 密码不重复,判断账号密码长度是否在 6-18位之间
if 6 <= len(username) <= 18 and 6 <= len(password1) <= 18:
# 注册账号
users.append({'user': username, 'password': password2})
return {"code": 1, "msg": "注册成功"}
else:
# 账号密码长度不对,注册失败
return {"code": 0, "msg": "账号和密码必须在6-18位之间"}

 

posted @ 2022-11-16 14:28  千秋与  阅读(6)  评论(0编辑  收藏  举报