首页 > 编程语言 >Python面向对象+unittest框架(存档)

Python面向对象+unittest框架(存档)

时间:2022-09-18 19:33:06浏览次数:79  
标签:__ 代码 Python unittest 面向对象 对象 print self def

面向对象

基本的介绍

面向对象是一个编程思想(写代码的套路)
编程思想:
1.面向过程
2.面向对象
以上两种都属于写代码的套路(方法),最终目的都是为了将代码书写出来,只不过过程和思考方法不太一样.
  • 面向过程
    • 关注的是具体步骤的实现,所有的功能都自己书写
    • 亲力亲为
    • 定义一个个函数,最终按照顺序调用函数
  • 面向对象
    • 关注的是结果,谁(对象)能帮我做这件事
    • 偷懒
    • 找一个对象(),让对象去做

类和对象

面向对象的核心思是  找一个对象去帮我们处理事情
在程序代码中  对象是由 类 创建的

类和对象,是面向对象编程思想中非常重要的两个概念
    • 抽象的概念,对多个特征和行为相同或者相似事物的统称。
    • 泛指的(指代多个,而不是具体的一个)
  • 对象

    • 具体存在的一个事物,看得见摸得着的

    • 特指的,(指代一个)

苹果 --->类
红苹果 ---->类
张三嘴里正在吃的那个苹果 --->对象

类的组成

  1. 类名(给这多个事物起一个名字,在代码中满足大驼峰命名法(每个单词的首字母大写))
  2. 属性〈事物的特征,即有什么,一般文字中的名词)
  3. 方法(事物的行为,即做什么事,一般是动词)

类的抽象(类的设计)

  • 类的抽象,其实就是找到类的 类名 属性 和方法

  • 举例:

    • 需求:
      小明今年18岁,身高1.75,每天毕上跑完步,会去吃东西
      小美今年17岁,身高1.65,小美不跑步,小美喜欢吃东西
    类名: 人类(Person,People)
    属性: 名字(name),年龄(age),身高(height)
    方法: 跑步(run)  吃(eat)
    
    • 需求:
      一只黄颜色的狗狗叫大黄看见生人汪汪叫
      看见家人摇尾巴
    类名: 狗类(Dog)
    属性: 颜色(color),名字(name)
    方法: 汪汪叫(bark),摇尾巴(shake)
    

面向代码的步骤

  1. 定义类,在定义类之前先设计类
  2. 创建对象,使用第一步定义的类创建对象
  3. 通过对象调用方法

面向对象基本代码的书写

1. 定义类

  • 先定义简单的类,不包含属性,在 python中定义类需要使用 关键字class

  • 方法:方法的本质是在类中定义的函数,只不过,第一个参数是 self

    class类名:
    #在缩进中书写的内容,都是类中的代码
    
    def方法名(self):   #就是一个方法
    	pass 
    

2. 创建对象

  • 创建对象是使用 类名() 进行创建,即

    类名()  # 创建一个对象,这个对象在后续不能使用
    # 创建的对象想要在后续的代码中继续使用,需要使用一个变量,将这个对象保存起来
    
    变量=类名()  # 这个变量中保存的是对象的地址,一般可以成为这个变量为对象
       
    # 一个类可以创建多个对象,只要出现类名()就是创建一个对象,每个对象的地址是不一样的
    

3. 调用方法

对象.方法名()

列表.sort()
列表.append()

4. 案例实现

  • 需求:小猫爱吃鱼,小猫要喝水

    class Cat:   # 类名
    
        def eat(self):  # 缩进中书写方法
            print('小猫爱吃鱼')
    
        def drink(self):
            print('小猫爱喝水')
    
    
    # 创建对象1
    blue_Cat = Cat()
    blue_Cat.eat()  # 调用方法
    blue_Cat.drink()
    
    # 创建对象2
    black_Cat = Cat()
    black_Cat.eat()
    black_Cat.drink()
    

self 的说明

class Cat:   # 类名

    def eat(self):  # 缩进中书写方法
        print('小猫爱吃鱼')
        
black_Cat.eat()
1.从函数的语法上讲,self是形参,就可以是任意的变量名,只不过我们习惯性将这个形参写作self

2. self 是普通的形参,但是在调用的时候没有传递实参值,原因是,Python解释器在执行代码的时候,自动的将调用这个方法的对象传递给了self,即self的本质是对象

3.验证,只需要确定通过哪个对象调用,对象的引用和 self 的引用是一样的

4. self是函数中的局部变量,直接创建的对象是全局变量
class Cat:
	#在缩进中书写方法
	def eat(self):  # self 会自动出现,暂不管
	print(f'{id(self)}, self')
	print('小猫爱吃鱼...')
    
# 2.创建对象
blue_ cat = Cat()
print(f'{id(blue_ cat)}, blue_ cat')

# 3.通过对象调用类中的方法
blue_ cat.eat() # blue_ cat对象调用eat 方法,解释器就会将blue_ cat对象传给self

print('*_' * 30)

#创建对象
black_ cat = Cat()
print(f"{id(black_ cat)}, black_ cat") 
black_ cat.eat() # black_cat 对象调用eat 方法,解释器就会将black_cat 对象传给self

对象的属性操作

添加属性

对象.属性名=属性值
  • 类内部添加

    在内部方法中,self 是对象,
    self.属性名=属性值
    
    #在类中添加属性一般写作__init_方法中
    
  • 类外部添加

    对象.属性名 = 属性值   # 一般不使用
    

获取属性

对象.属性名
  • 类内部

    在内部方法中,self是对象,
    self.属性名
    
  • 类外部

    对象.属性名   # 一般很少使用
    
class cat:
	#在缩进中书写方法
	def eat(self): # self会自动出现,暂不管
	print(f' {id(self)},self')
	print(f'小猫{self.name}爱吃鱼...")

# 2.创建对象
blue_cat = cat()
print(f"{id(blue_cat)}, blue_cat')  
# 给蓝猫添加name属性
blue_cat.name ="蓝猫"
      
# 3.通过对象调用类中的方法
blue_cat.eat() # blue_cat对象调用eat方法,解释器就会将blue_cat对象传给self
print( '_*_' * 30)
##创建对象
black_cat = cat()
black_cat.name ='黑猫'
print(f"{id(black_cat)}, black_cat")
black_cat.eat() # black_cat对象调用eat方法,解释器就会将black_cat对象传给self

魔法方法

  • python中有一类方法,以两个下划线开头,两个下划线结尾,并且在满足某个条件的情况下,会自动调用,这类方法称为魔法方法

  • 学习:
    1.什么情况下自动调用

    2.有什么用,用在哪

    3.书写的注意事项

__init__ 方法 **

  1. 什么情况下自动调用

    • 创建对象之后会自动调用
  2. 有什么用,用在哪

    1. 给对象添加属性的,(初始化方法/ 相当于其他语言的 构造方法)
    2. 某些代码,在每次创建对象之后,都要执行,就可以将这行代码写在__init__方法
  3. 书写的注意事项:

    • 不要写错了
    • 如果 init 方法中,存在出了 self 之外的参数,在创建对象的时候必须传参
class Cat:
    # 定义添加属性的方法

    def __init__(self, name, age):  # 这个方法是创建对象之后调用
        self.name = name  # 给对象添加name属性
        self.age = age  # 给对象添加age属性
        print('我被调用了')

    # 输出属性信息
    def show_info(self):
        print(f'小猫的名字是:{self.name},年龄是:{self.age}')


# Cat('小猫', 2)  # 创建对象,会输出
cute_Cat = Cat('蓝猫', 5)  # 创建对象,会输出
blue = cute_Cat  # 不是创建对象,不会输出
blue.show_info()

black_Cat = Cat('黑猫',8)
black_Cat.show_info()

__str__方法 *

  1. 什么情况下自动调用

    • 使用print(对象)打印对象的时候会自动调用
  2. 有什么用,用在哪

    • 在这个方法中一般书写对象的属性信息的,即打印对象的时候想要查看什么信息,在这个方法中进行定义的
    • 如果类中没有定义__str__方法,print(对象),默认输出对象的引用地址
  3. 书写的注意事项

    • 这个方法必须 返回 一个字符串
class Cat:
    # 定义添加属性的方法

    def __init__(self, name, age):  # 这个方法是创建对象之后调用
        self.name = name  # 给对象添加name属性
        self.age = age  # 给对象添加age属性
        print('我被调用了')

    def __str__(self):
        # 方法必须返回一个字符串,只要是字符串就行
        return f'小猫的名字是:{self.name},年龄是:{self.age}'


# Cat('小猫', 2)  # 创建对象,会输出
cute_Cat = Cat('蓝猫', 5)  # 创建对象,会输出
blue_Cat = cute_Cat  # 不是创建对象,不会输出
print(blue_Cat)

black_Cat = Cat('黑猫', 8)
print(black_Cat)

__del__方法 [了解]

__init_方法,创建对象之后,会自动调用 (构造方法)
__del__方法,对象被删除销毁时,自动调用的(遗言,处理后事)  (析构方法)

1.调用场景,程序代码运行结束,所有对象都被销毁
2.调用场景,直接使用del删除对象(如果对象有多个名字(多个对象引用一个对象),需要把所有的对象都删除才行)
class Demo:
    def __init__(self, name):
        print('我是__init_,我被调用了')
        self.name = name

    def __del__(self):
        print(f' {self.name}没了,给他处理后事... ')


# Demo('a')
a = Demo('a')
b = Demo('b')
del a  # 删除销毁对象,
print('代码运行结束')

# 我是__init_,我被调用了
# 我是__init_,我被调用了
#  a没了,给他处理后事...
# 代码运行结束
#  b没了,给他处理后事...

案例

  • 需求:
    1. 小明体重75.0 公斤
    2. 小明每次跑步会减肥0.5 公斤
    3. 小明每次吃东西体重增加1公斤
类名:人类Person
属性:姓名name,体重weight
方法:跑步run
  吃东西eat
  添加属性__init__
  属性信息__str__
class Person:
    def __init__(self,name,weight):
        self.name = name
        self.weight = weight

    def __str__(self):
        return f'姓名:{self.name},体重{self.weight}kg'

    def run(self):
        print('小明跑了一次步,减重了')
        self.weight = self.weight-0.5

    def eat(self):
        print('小明吃了一顿大餐,重了')
        self.weight = self.weight+1


xm = Person('小明',75.0)
print(xm)
xm.run()
print(xm)
xm.eat()
print(xm)

案例2 分析

  • 需求:

    1. 房子(House)有户型、总面积和家具名称列表

      • 新房子没有任何的家具
    2. 家具(Houseltem)有名字和占地面积,其中

      • 席梦思(bed)占地4平米

      • 衣柜(chest)占地:2平米。

      • 餐桌(table)占地1.5平米

    3. 将以上三件 家具 添加到 房子 中

    4. 打印房子时,要求输出 : 户型、总面积、剩余面积、家具名称列表

  • 剩余面积

    1. 在创建房子对象时,定义一个剩余面积的属性,初始值和总面积相等
    2. 当调用add_item方法,向房间添加家具时,让剩余面积 - = 家具面积
类名:房子类 House
属性:户型name,总面积total_area,剩余面积 free_area = total_area
	家具名称列表 item_list =[]
方法:__init__, __str__
添加家具方法
def add_item(self, item):    #item家具对象
	先判断房子的剩余面积和总面积的关系
	修改房子的剩余面积
	修改房子的家具名称列表
类名:家具类 HouseItem
属性:名字name,占地面积area
方法:__init__, __str__

作业例题

  • 题目一:

    image-20220909160615641

  • 代码:

    class Student:
        def __init__(self,name,age):
            self.name=name
            self.age = age
    
        def __str__(self):
            return f'姓名:{self.name},年龄:{self.age}岁'
    
        def eat(self):
            print(f'{self.name}要吃饭')
    
        def sleep(self):
            print(f'{self.name}要睡觉')
    
        def year(self):
            self.age = self.age+1
            print(self.age)
    
    
    xm = Student('小明', 18)
    print(xm)
    xm.eat()
    xm.sleep()
    xm.year()
    
    xh = Student('小红', 17)
    print(xh)
    xh.eat()
    xh.sleep()
    xh.year()
    
  • 题目二

    image-20220909174227304

  • 代码:

    class HouseItem:
        def __init__(self, name, area):
            self.name = name
            self.area = area
    
        def __str__(self):
            return f'{self.name}占地{self.area}平米'
    
    
    class House:
        def __init__(self, house_type, area):
            self.house_type = house_type
            self.total_area = area
            self.free_area = area
            self.item_list = []
    
        def __str__(self):
            return f'户型为{self.house_type},总面积为{self.total_area},剩余面积{self.free_area},家具名称列表{self.item_list}'
    
        def add_item(self, item):
            if self.free_area > item.area:
                self.item_list.append(item.name)
                self.free_area = self.free_area - item.area
                print(f'{item.name} 添加成功')
            else:
                print('添加家具失败')
    
    
    bed = HouseItem('席梦思', 4)
    chest = HouseItem('衣柜', 2)
    table = HouseItem('餐桌', 1.5)
    print(bed)
    print(chest)
    print(table)
    
    house = House('三室一厅', 150)
    house.add_item(bed)
    print(house)
    

私有和公有

  1. 在Python中定义的方法和属性,可以添加访问控制权限(即在什么地方可以使用这个属性和方法)

  2. 访问控制权限分为两种,公有权限,私有权限

  3. 公有权限

    • 直接书写的方法和属性,都是公有的

    • 公有的方法和属性,可以在任意地方访问和使用

  4. 私有权限

    • 在类内部,属性名或者方法名前边加上两个下划线,这个属性或者方法就变为私有的
    • 私有的方法和属性,只能在当前类的内部使用
  5. 什么时候定义私有

    1. 某个属性或者方法,不想在类外部被访问和使用,就将其定义为私有即可
    2. 测试中,一般不怎么使用,直接公有即可
    3. 开发中,会根据需求文档,确定什么作为私有
  6. 如果想要在类外部操作私有属性,方法是,在类内部定义共有的方法,我们通过这个方法去操作

  • 案例

    定义一个Person类,属性name,age(私有)
    
    class Person:
        def __init__(self, name, age):
            self.name = name  # 姓名
            # 私有的本质,是 Python解释器执行代码,发现属性名或者方法名前有两个_,
            # 会将这个名字重命名#会在这个名字的前边加上_类名前缀,即self._age ===> self._Person__age
            self.__age = age  # 年龄,将其定义为私有属性,属性名前加上两个_
    
        def __str__(self):  # 在类内部可以访问私有属性的
            return f'名字: {self.name},年龄: {self.__age}'
    
    
    xm = Person('小明', 18)
    print(xm)
    # 在类外部直接访问age属性
    # print(xm._age)#会报错,在类外部不能直接使用私有属性#直接修改age属性
    xm.__age = 20  # 这个不是修改私有属性,是添加了一个公有的属性_age
    print(xm)
    print(xm._Person__age)  # 能用但是不要用
    xm._Person__age = 19
    print(xm)
    
    

继承

  1. 继承描述的是类与类之间的关系
  2. 继承的好处:减少代码的冗余(相同的代码不需要多次重复书写),可以直接使用

语法

#class A(object) :
class A:   #没有写父类,但也有父类,object,object类是 Python中最顶级(原始)的类
	pass
class B(A):    #类B,继承类A
	pass
  • 术语

    1. A类,称为是父类(基类)

    2. B类,称为是子类(派生类)

    单继承:一个类只继承一个父类,称为单继承

  • 继承之后的特点:

    • 子类 (B) 继承父类 (A) 之后,子类的对象可以直接使用父类中定义的公有属性和方法
  • 案例:

    1.定义一个动物类,吃

    2.定义一个狗类,继承动物类,吃,叫

    3.定义一个哮天犬类,继承狗类

  • 代码

    class Animal:
        def eat(self):
            print('吃东西')
    
    
    class Dog(Animal):
        def dark(self):
            print('嚎叫')
    
    
    class XTQ(Dog):
        print('我是哮天犬')
    
    
    xtq = XTQ()
    xtq.dark()
    xtq.eat()
    
  • 结论

    python中对象.方法()调用方法
    1.现在自己的类中的去找有没有这个方法如果有,直接调用
    2.如果没有去父类中查找,如果有,直接调用
    3.如果没有,去父类的父类中查找,如果有直接调用
    4 ...
    5.如果object类中有,直接调用,如果没有,代码报错
    

重写

  • 重写:在子类中定义了和父类中名字相同的方法,就是重写
  • 重写的原因:父类中的方法,不能满足子类对象的需求,所以重写
  • 重写之后的特点:调用子类字节的方法,不再调用父类中的方法重写的方式:
    • 1.覆盖(父类中功能完全抛弃,不要,重写书写)
    • 2.扩展(父类中功能还调用,只是添加一些新的功能)(使用较多)

覆盖

  1. 直接在子类中定义和父类中名字相同的方法

  2. 直接在方法中书写新的代码

class Dog:
	def bark( self):
		print( '汪汪汪叫..... ')
class xTQ(Dog):
	# XTQ类bark方法不再是汪汪汪叫,改为嗷嗷嗷叫
    def bark(self):
		print( '嗷嗷嗷叫...')

        
xtq = xTQ()
xtq.bark()

扩展父类中的功能

  1. 直接在子类中 定义和父类中名字相同的方法
  2. 在合适的地方调用父类中方法 super.( ) . 方法( )
  3. 书写添加的新功能

多态

  1. 是一种写代码调用的一种技巧
  2. 同一个方法,传入不同的对象,执行得到不同的结果,这种现象称为是多态
  3. 多态可以增加代码的灵活度
  • 哪个对象调用方法,就去自己的类中去查找这个方法,找不到去父类中找

属性和方法

Python中一切皆对象.
即使用class 定义的类也是一个对象

对象的划分

实例对象(实例)

  1. 通过类名()创建的对象,我们称为实例对象,简称实例
  2. 创建对象的过程称为是类的实例化
  3. 我们平时所说的对象就是指实例对象(实例)
  4. 每个实例对象,都有自己的内存空间,在自己的内存空间中保存自己的属性(实例属性)

类对象(类)

  1. 类对象就是类,或者可以认为是类名
  2. 类对象是Python解释器在执行代码的过程中创建的
  3. 类对象的作用:①使用类对象创建实例类名(),②类对象也有自己的内存空间,可以保存一些属性值信息(类属性)
  4. 在一个代码中,一个类只有一份内存空间

属性的划分

实例属性

  • 概念:是实例对象具有的属性

  • 定义和使用:

    在init方法中,使用 self.属性名=属性值 定义
    在方法中是使用 self.属性名 来获取(调用)
    
  • 内存

    • 实例属性,在每个实例中都存在一份
  • 使用时机

    1.基本上99%都是实例属性,即通过self 去定义
    2.找多个对象,来判断这个值是不是都是一样的,如果都是一样的,同时变化,则一般定义为类属性,否则定义为实例属性
    

类属性

  • 概念:是类对象具有的属性

  • 定义和属性

    在类内部,方法外部,直接定义的变量,就是类属性
    
    使用:类对象.属性名=属性值  or  类名.属性名=属性值
    类对象.属性名  or  类名.属性名
    
  • 内存

    • 只有类对象中存在一份

方法的划分

  • 方法,使用def 关键字定义在类中的函数就是方法

实例方法(最常用)

  • 定义

    #在类中直接定义的方法就是实例方法
    class Demo:
    	def func(self):  #参数一般写作self,表示的是实例对象
    		pass
    
  • 定义时机(什么时候用)

    如果在方法中需要使用 实例属性(即需要使用self),则这个方法 必须 定义为实例方法
    
  • 调用

    对象.方法名()   #不需要给self 传参
    

类方法(会用)

  • 定义

    #在方法名字的上方书写@classmethod装饰器(使用@classmethod装饰的方法)
    class Demo:
    	@classmethod
    	def func(cls):   #参数一般写作cls,表示的是类对象(即类名) class
    		pass
    
  • 定义时机(什么时候用)

    1.前提,方法中不需要使用 实例属性(即self)
    2.用到了 类属性,可以将这个方法定义为 类方法,(也可以定义为 实例方法)
    
  • 调用

    # 1.通过类对象调用
    类名.方法名()  #也不需要给cls传参,python解释器自动传递
    
    # 2.通过实例对象调用
    实例.方法名()  #也不需要给cls传参,python解释器自动传递
    

静态方法

  • 定义

    #在方法名字的上方书写@staticmethod装饰器(使用@staticmethod装饰的方法)
    class Demo:
    	@staticmethod
    	def func():    #一般没有参数
    		pass
    
  • 定义时机(什么时候用)

    1.前提,方法中不需要使用实例属性(即self)
    2.也不使用 类属性,可以将这个方法定义为 静态方法
    
  • 调用

    # 1.通过类对象调用
    类名.方法名()
    
    # 2.通过实例对象调用
    实例.方法名()
    

练习

  • 题目1

    定义一个游戏类Game,包含实例属性玩家名字(name

    1. 要求记录游戏的最高分(top_score类属性),

    2. 定义方法: show_help显示游戏的帮助信息输出这是游戏的帮助信息

    3. 定义方法: show_top_score,打印输出游戏的最高分

    4. 定义方法: start_game,开始游戏,规则如下

      1. 使用随机数获取本次游戏得分范围(10 - 100 )之间
      2. 判断本次得分和最高分之间的关系
        • 如果本次得分比最高分高
          • 修改最高分
        • 如果分数小于等于最高分,则不操作
      3. 输出本次游戏得分
    5. 主程序步骤:

      1.创建一个Game对象, 小王
      2.小王玩一次游戏
      3.查看历史最高分
      4.小王再玩一次游戏
      5.查看历史最高分
      6.查看游戏的帮助信息
      
  • 代码

    import random
    
    
    class Game:
        top_score = 0
    
        def __init__(self, name):
            self.name = name
    
        def show_help(self):
            print('这是游戏的帮助信息')
    
        def show_top_score(self):
            print(f'游戏的最高分为{Game.top_score}')
    
        def start_game(self):
            print(f'{self.name}开始一局游戏', end=' ')
            score = random.randint(10, 100)
            if score > Game.top_score:
                Game.top_score = score
            print(f'{self.name}本次游戏得分为{score}')
    
    
    xw = Game('小王')
    xw.start_game()
    xw.show_top_score()
    xw.start_game()
    xw.show_top_score()
    xw.show_help()
    
    • 优化(使用类方法和静态方法)
        @staticmethod
        def show_help():
            print('这是游戏的帮助信息')
    
        @classmethod
        def show_top_score(cls):
            print(f'游戏的最高分为{cls.top_score}')
    

补充

  • 哈希(hash) :是一个算法,可以对数据产生一个唯一的值(指纹)

  • is 可以用来判断两个对象是不是同一个对象,即两个对象的引用是否相同

    a is b    === >   id(a)  ==  id(b)
    
    面试中可能会问: is和 == 的区别?
    ==  只判断数据值是否相同, is判断引用是否相同
    

文件介绍

  • 计算机的文件,就是存储在某种长期储存设备上的一段数据

  • 作用:将数据长期保存下来,在需要的时候使用

    1. 计算机只认识二进制(0 1)
    2. 文件中存储的数据都是以二进制(0 1)的形式去存储的
  • 可以根据文件中的二进制内容,能否使用记事本软件将其转换为文字,将文件分为两种:文本文件和二进制文件

  • 文本文件:

    • 能够使用记事本软件打开(能够使用记事本转换为文字)

      txt,md, py html, css, js , json
      
  • 二进制文件

    • 不能使用记事本软件打开的

       exe,mp3,mp4, jpg,png
      

文件操作

文件操作的步骤

1.打开文件
2.读或者写文件
3.关闭文件

1. 打开文件

打开文件:将文件从磁盘(硬盘)中读取到内存中
语法:
open(file, mode= 'r ', encoding=None)
  • 参数file:是要打开的文件,类型是字符串,文件的路径可以是相对路径,也可以是绝对路径(从根目录开始书写的路径),建议使用相对路径(相对于当前代码文件所在的路径,./ ../ )
  • 参数mode:默认参数(缺省参数),表示的是打开文件的方式
    • r: read 只读打开
    • w : write 只写打开
    • a: append 追加打开,在文件的末尾写入内容
  • 参数encoding:编码方式,(文字和二进制如何进行转换的)
    • gbk:将一个汉字转换为2个字节二进制
    • utf-8:常用,将一个汉字转换为3个字节的二进制
  • 返回值:返回的是文件对象,后续对文件的操作,都需要这个对象

2. 读或者写文件

写文件

  • 向文件中写入指定的内容

    前提:文件的打开方式是 w 或者 a

文件对象.write(' 写入文件的内容 ')
# 返回值:写入文件的字符数,一般不关注

#注意w方式打开文件:
1,文件不存在,会直接创建文件
2.文件存在,会覆盖原文件(将原文件中的内容清空)
  • 代码

    f = open('a.txt', 'w', encoding= 'utf-8')
    f.write('天天向上\n')
    f.write('好好学习')
    f.close()
    

读文件

  • 将文件中的内容读取出来

    前提:文件的打开方式需要是 r

文件对象.read(n)

#参数n表示读取多少个字符,一般不写,表示读取全部内容
#返回值:读取到的文件内容,类型字符串
#1,打开文件
f = open( 'a.txt ', 'r', encoding='utf-8')
#2,读文件
buf = f.read()
print(buf)  #目前只是打印读取的内容,可以做其它的事
#3.关闭文件
f.close()

# r方式打开文件,如果文件不存在,代码会报错

3. 关闭文件

  • 关闭文件:将文件占用的资源进行清理,同时会保存文件,文件关闭之后,这个文件对象就不能使用了

    文件对象.close()
    

使用with open 打开文件

with open()打开文件的好处:不用自己去书写关闭文件的代码,会自动进行关闭
with open(file, mode, encoding='utf-8') as 变量:
	#在缩进中去读取或者写入文件

#缩进中的代码执行结束,出缩进之后,文件会自动关闭
  • 代码

    with open('a.txt', 'a', encoding= 'utf-8') as f:
        f.write('good good study')
    
    
    # a方式打开文件,文件不存在会创建文件,文件存在,在文件的末尾写入内容
    
  • 代码(读文件的三种方式)

    with open('b.txt', encoding='utf-8') as f:
        buf = f.readline()
        print(buf)
        print(f.readline())
    
    with open('b.txt', encoding='utf-8') as f:
        for i in f:    # 按行读取,读到文件末尾结束
            print(i, end='')
    
    with open('b.txt', encoding='utf-8') as f:
        
        # read()和readline()读到文件末尾,返回一个空字符串,即长度为0
        while True:
            buf = f.readline()
            if len(buf) == 0:
                break
            else:
                print(buf, end='')
    
    #在容器中,容器为空,即容器中的数据的个数为0 ,表示 False,其余情况都是True
    with open('b.txt', encoding='utf-8 ') as f:
    	while True:
    		buf = f.readline()
    		if buf:   # if Len( buf) != 0
                print(buf)
    		else:
    			break
    

json 文件的处理

  • json文件也是一个文本文件,就可以直接使用 read()write() 方法去操作文件,只是使用这两个方法不方便,所以对json文件有自己独特的读取和写入的方法
  • 常用在做测试的时候,将测试数据定义为json文件格式,使用代码读取json文件,即读取测试数据,进行传参(参数化)

json的介绍

  • json基于文本,独立于语言的轻量级的数据交换格式
    • 基于文本,是一个文本文件,不能包含图片,音视频等
    • 独立于语言,不是某个语言特有的,每种编程语言都可以使用的
    • 轻量级,相同的数据,和其他格式相比,占用的大小比较小
    • 数据交换格式,后端程序员给前端的数据(json、html、xml)

json文件的语法

  1. json文件的后缀是.json
  2. json中主要数据类型为对象( { } 类似 Python中字典)和 数组 ( [ ] ,类似 Python中的列表 ),对象和数组可以互相嵌套
  3. 一个json文件是一个对象或者数组(即json文件的最外层要么是一个 { } ,要么是一个数组 [ ]
  4. json中的对象是由键值对组成的,每个数据之间使用 逗号 隔开,但是最后一个数据后边不要写逗号
  5. json中的字符串必须使用 双引号
  6. json中的其他数据类型(右边是python中的)
    • 数字类型 ----> int float
    • string 字符串 ---> str
    • 布尔类型 true, false -----> True,False
    • null ----> None

json文件的书写

我叫小明,我今年18岁,性别男,爱好听歌,游戏,购物,吃饭,睡觉,打豆豆T我的居住地址为国家中国,城市上海
{
  "name": "小明",
  "age": 18,
  "isMen": true,
  "like": [
    "听歌",
    "游戏",
    "购物",
    "吃饭",
    "睡觉",
    "打豆豆"
  ],
  "address": {
    "country": "中国",
    "city": "上海"
  }
}

读取 json 文件

1.导包 import json

2.读打开文件

3.读文件

json.load(文件对象)
# 返回的是 字典 (文件中是对象)  或者 列表 (文件中是数组)
import json

with open('info.json', encoding='utf-8') as f:
    # buf = f.read()   # <class 'str'>
    result = json.load(f)
    print(type(result))  # <class 'dict'> --> 更好取值

    print(result.get('name'))
    print(result.get('age'))
    print(result.get('address').get('city'))

练习

题目1

我叫小明,我今年18岁,性别男,爱好听歌,游戏,吃饭,睡觉,打豆豆,我的居住地址为国家中国,城市上海.
我叫小红,我今年17岁,性别女,爱好听歌,学习,购物我的居住地址为国家中国,城市北京.
  • info2.json

    [
      {
      "name": "小明",
      "age": 18,
      "isMen": true,
      "like": [
        "听歌",
        "游戏",
        "购物",
        "吃饭",
        "睡觉",
        "打豆豆"
      ],
      "address": {
        "country": "中国",
        "city": "上海"
      }
    },
      {
      "name": "小红",
      "age": 17,
      "isMen": false,
      "like": [
        "听歌",
        "学习",
        "购物"
      ],
      "address": {
        "country": "中国",
        "city": "北京"
      }
    }
    ]
    
  • 代码

    import json
    
    with open('info2.json', encoding='utf-8') as f:
        info_list = json.load(f)
        for info in info_list:
            print(info.get('name'), info.get('age'), info.get('address').get('city'))
    
    

题目2

某网站的测试数据如下data.json,需求,提取json文件中的用户名,密码和预期结果,组成如下格式:[(),(),()]  (自动化参数化需要的数据格式)
[
  {
    "desc":"正确的用户名密码",
    "username": "admin",
    "password" : "123456",
    "expect":"登录成功"
  },

  {
    "desc":"错误的用户名",
    "username": "root",
    "password" : "123456",
    "expect":"登录失败"
  },

  {
    "desc":"错误的密码",
    "username" : "admin",
    "password" : "123123",
    "expect":"登录失败"
  }
  
]
  • 代码(!!重要!常用)

    import json
    
    
    def read_data():
        new_list = []
        with open('data.json', encoding='utf-8') as f:
            info = json.load(f)  # 列表
            for i in info:   # i 字典
                new_list.append((i.get('username'), i.get('password'), i.get('expect')))
    
        return new_list
    
    

json 的写入

文件对象.write(字符串)不能直接将Python的 列表 和 字典 作为参数传递
想要将Python中的数据类型存为json文件,需要使用json提供的方法,不再使用write

步骤:
1.导包import json
2.写(w)方式打开文件
3.写入

json.dump(Python中的数据类型,文件对象)
  • 代码

    import json
    
    my_list = [('admin', '123456', '登录成功'), ('root', '123456', '登录失败'), ('admin', '123123', '登录失败')]
    
    with open('info_4.json', 'w', encoding='utf-8') as f:
        # json.dump(my_list,f)
        # json.dump(my_list, f, ensure_ascii=False)  # 直接显示中文,不以ASCII的方式显示
    
        # 显示缩进
        json.dump(my_list, f ,ensure_ascii=False, indent=4)
    
  • 注意

    image-20220914145821529

异常

  • 程序在运行时,如果Python解释器遇到到一个错误,会停止程序的执行,并且提示一些错误信息,这就是异常
  • 程序停止执行并且提示错误信息这个动作,抛出异常(raise关键字)
  • 捕获异常:程序遇到异常,默认动作是终止代码程序的执行,遇见异常之后,可以使用异常捕获,让程序代码继续运行,不会终止运行(重点)

image-20220914150841434

异常捕获 [ 重点 ]

基本语法
try:
	书写可能发生异常的代码
except:   #任何类型的异常都能捕获
	发生了异常执行的代码
    
    
try:
	书写可能发生异常的代码
except 异常类型:    #只能捕获指定类型的异常,如果不是这个异常,还是会报错
	发生了异常执行的代码
  • 举例1

    try:
        # 1、获取用户从键盘输入的数据
        num = input('请输入数字:')
        # 2.转换数据类型为整数
        num = int(num)
        # 3.输出转换之后的数据内容
        print(num)
    except:
        print('请输入正确的数字')
    
    print('后续其他的代码,可以继续执行')
    
    
  • 举例2

    try:
        num = input('请输入数字')
        num = int(num)
        print(num)
        a = 10/num    # num 不能等于0,但是num=0的情况不属于ValueError
        print(f'a:{a}')
    
    except ValueError:
        print('发生了异常,请输入正确的数字...')
    
    print('后面的代码依旧会执行')
    

捕获多个指定类型的异常

  • 好处:可以针对不同的异常错误,进行单独的代码处理

    try:
    	书写可能发生异常的代码
    except 异常类型1:    #只能捕获指定类型的异常,如果不是这个异常,还是会报错
    	发生了异常1执行的代码
    except 异常类型2:
    	发生了异常2执行的代码
    except 异常类型...:
    	发生了异常...执行的代码
    
    
    try:
        num = input('请输入数字')
        num = int(num)
        print(num)
        a = 10/num    # num 不能等于0,但是num=0的情况不属于ValueError
        print(f'a:{a}')
    
    except ValueError:
        print('发生了异常,请输入正确的数字...')
    except ZeroDivisionError:
        print('输入数字为除数,不能为0')
    
    print('后面的代码依旧会执行')
    

异常捕获的完整版本

  • 完整版本中的内容,不是说每一次都要全部书写,根据自己的需要,去选择其中的进行使用
try:
	可能发生异常的代码
except 异常类型1:
	发生异常类型1执行的代码

# Exception是常见异常类的父类,这里书写 Exception,可以捕获常见的所有异常,as变量,这个变量是一个异常类的对象,print(变量)可以打印异常信息
except Exception as 变量:
	发生其他类型的异常,执行的代码
else:
	没有发生异常会执行的代码
finally:
	不管有没有发生异常,都会执行的代码

  • 常用:

    try:
    	可能发生异常的代码
    except Exception as e:
    	发生异常执行的代码
    

总结

pytest ui自动化课程中进行学习

在windows中绝对路径路径可能存在的问题:
c :/xx/ ddd
c : \xxx\xxx   可能会出现\和后边的字符结合组成转义字符
c : \xxx\Xx
r"c : \xx\xx"

练习

题目5
按照如下要求完成代码:
1 使用随机数产生10个1-20之间的数字
2 将这些数字保存到文件data.txt中
3 读取data.txt中的内容,按照数字大小进行降序排序
4 将最大的五个数字存入文件data1.txt中
  • 另一种写法:

    先将10个数字,转为字符串,存入列表
    ',' .join(列表)
    
  • 使用json文件:

    import random
    import json
    
    my_list = []
    for i in range(10):
        num = random.randint(1, 20)
        my_list.append(num)
    
    with open('data.json', 'w', encoding='utf-8') as f:
        json.dump(my_list, f)
    
    with open('data.json', 'r', encoding='utf-8') as f:
        data_list = json.load(f)
        # 排序
        data_list.sort(reverse=True)
        print(data_list[:5])
    
    with open('data1.txt', 'w', encoding='utf-8') as f:
        for i in range(5):
            f.write(str(data_list[i]))
    
    
题目6
两种方式实现以下需求(1.异常捕获︰2.)

1.获取用户输入的数字
2.判断获取的数字是否整数
3.如果不是整数,提示输入错误
4.如果是整数,则进一步判断是奇数还是偶数
5.最终提示:程序运行结束
  • 代码:(两种方法)

    def func1():
        num = input('请输入数字')
        try:
            num = int(num)
        except Exception as e:
            print('输入错误,', e)
        else:
            if num % 2 == 0:
                print('偶数')
            else:
                print('奇数')
        finally:
            print('程序运行结束')
    
    
    def func2():
        num = input('请输入数字')
        if num.isdigit():
            num = int(num)
            if num % 2 == 0:
                print('偶数')
            else:
                print('奇数')
        else:
            print('输入错误')
        print('程序运行结束')
    
    

异常传递 [ 了解 ]

异常传递是Python中已经实现好了,我们不需要操作,我们知道异常会进行传递.

异常传递:在函数嵌套调用的过程中,被调用的函数,发生了异常,如果没有捕获,会将这个异常向外层传递.....如果传到最外层还没有捕获,才报错

模块和包

1. Python源代码文件就是一个模块
2.模块中定义的变量函数类,都可以让别人使用,同样,可以使用别人定义的(好处:别人定义好的不需要我们再次书写,直接使用即可)
3.想要使用别人的模块中的内容工具(变量,类,函数),必须先导入模块才可以
4.我们自己写的代码,想要作为模块使用,代码的名字需要满足标识符的规则(由数字,字母下划线组成,不能以数字开头)

导入模块的语法

方式一 [ 重点记忆 ]

import 模块名
# 使用模块中的内容
模块名.工具名

# 举例
import random
import json
random.randint(a, b)
json.load()
json.dump()

方式二 [ 重要 - 快捷方式导包 ]

from 模块名 import 工具名
# 使用
工具名   # 如果是函数和类,需要加括号

# 举例
from random import randint
from json import load,dump
randint(a,b)
load()
dump()
快捷导入:Alt + Enter

image-20220914164916650

方式三 [了解] 基本不用

from 模块名 import *   #将模块中所有的内容都导入

from random import *
from json import *

randint(a,b)
load()
dump()
  • 问题:可能存在多个模块中有相同的名字的工具,会产生冲突

as : 起别名

  • 对于导入的模块和工具可以使用 as 关键字给其起别名
  • 注意:如果起别名,原来的名字就不能用了,只能使用别名

模块的查找顺序

  • 在导入模块的时候会先在当前目录中找模块,

    • 如果找到,就直接使用

    • 如果没有找到回去系统的目录中进行查找找到,直接使用

    • 没有找到,报错

  • 注意点:
    定义代码文件的时候,你的代码名字不能和你要导入的模块名字相同

__name__的作用

  1. 每个代码文件都是一个模块

  2. 在导入模块的时候,会执行模块中的代码(三种方法都会)

  3. __name__变量

    3.1 __name__变量,是python解释器自动维护的变量

    3.2 __name__变量,如果代码是直接运行,值是" __main__ "

    3.3 __name__变量,如果代码是被导入执行,值是 模块名(即代码文件名)

  4. 在代码文件中,在被导入时不想被执行的代码,可以写在if __name__=="__main__": 代码 的缩进中

image-20220914173025224

  • 左边导入运行,不执行右边的print
  • 右边直接运行,会执行全部的print

代码练习

1.定义一个模块tools.py
2.在模块中定义一个函数,func,输出'我是tools模块中的 funn函数'
3.在模块中定义一个类,Dog,具有属性name,age,方法 play,输出'xx在快乐的玩耍'
4.新建一个代码文件,调用tools模块中的 func函数并创建一个 Dog类的对象,调用play方法
def func():
    print('我是tools模块中的func函数')


class Dog:
    def __init__(self, name ,age):
        self.name = name
        self.age = age

    def play(self):
        print(f'{self.name}在快乐的玩耍')

import tools

tools.func()
dog = tools.Dog('小白',5)
dog.play()

image-20220914173840045

包(package)

  • 在Python中,包是一个目录,只不过在这个目录存在一个文件__init__.py(可以是空的)

    • 将功能相近或者相似的代码放在一起的
  • 在Python中使用的时候,不需要可以是区分是包还是模块,使用方式是一样的

  • random -- 模块(单个代码文件)

  • json -- 包(目录)

  • unittest -- 包(目录)

  1. import 包名
  2. alt 回车 快捷导入

UnitTest框架

介绍

  • 框架

    • 说明
    1. 框架英文单词framework 2.为解决一类事情的功能集合
      • 需要按照框架的 规定 (套路) 去书写代码
  • 什么是UnitTest框架?

    • 概念:UnitTest是Python自带的一个单元测试框架,用它来做单元测试。
      • 自带的框架(官方):不需要单外安装,只要安装了Python,就可以使用
        • random,json,os,time
      • 第三方框架:想要使用需要先安装后使用(pytest)
        • selenium , appium,requests
    • 单元测试框架:主要用来做单元测试,一般单元测试是开发做的.
    • 对于测试来说,unittest 框架的作用是 自动化脚本(用例代码) 执行框架(使用 unittest框架 来 管理 运行 多个测试用例的)
  • 为什么使用UnitTest框架?

    1. 能够组织多个用例去执行
    2. 提供丰富的断言方法(让程序代码代替人工自动的判断预期结果和实际结果是否相符)
    3. 能够生成测试报告
  • UnitTest 核心要素 (unitest的组成)

    1. TestCase(最核心的模块)

      Testcase(测试用例),注意这个测试用例是unittest框架的组成部分,不是手工和自动化中我们所说的用例(Test case)
      
      主要作用:每个Testcase(测试用例)都是一个代码文件,在这个代码文件中 来书写 真正的用例代码
      
      
    2. TestSuite

      TestSuite(测试套件),用来 管理 组装(打包)多个  Testcase (测试用例)的
      
    3. TestRunner

      TestRunner(测试执行,测试运行),用来执行TestSuite(测试套件)的
      
    4. TestLoader

      TestLoader(测试加载), 功能是对 Testsuite (测试套件) 功能的补充,管理 组装 (打包) 多个 Testcase (测试用例)的
      
    5. Fixture

      Fixture(测试夹具),书写在 Testcase(测试用例)代码中,是一个代码结构,可以在每个方法执行前后都会执行的内容
      
      举例:
      登录的测试用例,每个用例中重复的代码就可以写在 Fixture代码结构中,只写一遍,但每次用例方法的执行,都会执行Fixture中的代码
      1.打开浏览器
      2.输入网址
      
      

TestCase(测试用例)

  1. 是一个代码文件,在代码文件中来书写真正的用例代码
  2. 代码文件的名字必须按照标识符的规则来书写(可以将代码的作用在文件的开头使用注释说明)
  • 步骤

    1. 导包(unittest)
    2. 自定义测试类
    3. 在测试类中书写测试方法
    4. 执行用例
  • 代码

    """
    代码的目的:学习Testcase(测试用例)模块的书写方法
    """
    
    # 1,导包
    import unittest
    
    
    # 2,自定义测试类, 需要继承unittest模块中的Testcase类即可
    class TestDemo(unittest.TestCase):
        # 3,书写测试方法  即用例代码.目前没有真正的用例代码,使用print 代替
        # 书写要求,测试方法必须以test_开头(本质是以test开头)
        def test_method1(self):
            print('测试方法1')
    
        def test_method2(self):
            print('测试方法2')
    
    # 4,执行用例(方法)
    # 4.1 将光标放在类名的后边运行,会执行类中的所有的测试方法
    # 4.2将光标放在方法名的后边运行,只执行当前的方法
    
    

问题一 代码文件的命名不规范

1.代码文件的名字以数字开头
2.代码文件名字中有空格
3.代码文件名字有中文
4.其他的特殊符号
(数字,字母,下划线组成,不能以数字开头)

问题二 代码运行没有结果

右键运行没有 unittests for 的提示,出现的问题
解决方案:
方案1.重新新建一个代码文件,将写好的代码复制进去
方案2.删除已有的运行方式(如下图步骤)

image-20220914214816327

问题3 没有找到用例

测试方法中不是以test_开头的,或者单词写错了

TestSuite & TestRunner

Testsuite(测试套件):管理打包组装Testcase(测试用例)文件的TestRunner(测试执行):执行TestSuite(套件)
  • 步骤

    1. 导包(unittest)
    2. 实例化(创建对象)套件对象
    3. 使用套件对象添加用例方法
    4. 实例化运行对象
    5. 使用运行对象去执行套件对象
  • 代码

    """
    学习  Testsuite 和 Testrunner 的使用
    """
    
    # 1.导包(unittest)
    import unittest
    
    from Test_lzy.testcase1 import TestDemo1
    from Test_lzy.testcase2 import TestDemo2
    
    # 2.实例化(创建对象)套件对象
    suite = unittest.TestSuite()
    # 3.使用套件对象添加用例方法
    
    # 方法一:套件对象.addTest(测试类名.('方法名'))   # 建议测试类名和方法名直接去复制,不要手写
    suite.addTest(TestDemo1('test_method1'))
    suite.addTest(TestDemo1('test_method2'))
    suite.addTest(TestDemo2('test_method1'))
    suite.addTest(TestDemo2('test_method2'))
    
    # 4.实例化运行对象
    runner = unittest.TextTestRunner()   # 注意有Text
    # 5.使用运行对象去套执行套件对象
    # 运行对象.run(套件对象)
    runner.run(suite)
    

添加测试方法的另一种书写

  • 将—个测试类中的所有方法进行添加

  • 套件对象.addTest(unittest.makeSuite(测试类名))
    

代码:

  • """
    学习  Testsuite 和 Testrunner 的使用
    """
    
    # 1.导包(unittest)
    import unittest
    
    from Test_lzy.testcase1 import TestDemo1
    from Test_lzy.testcase2 import TestDemo2
    
    # 2.实例化(创建对象)套件对象
    suite = unittest.TestSuite()
    
    # 3.使用套件对象添加用例方法
    # 方法二:将—个测试类中的所有方法进行添加
    # 套件对象.addTest(unittest.makeSuite(测试类名))
    suite.addTest(unittest.makeSuite(TestDemo1))
    suite.addTest(unittest.makeSuite(TestDemo2))
    
    # 4.实例化运行对象
    runner = unittest.TextTestRunner()   # 注意有Text
    # 5.使用运行对象去套执行套件对象
    # 运行对象.run(套件对象)
    runner.run(suite)
    

查看测试结果(关注的内容)

image-20220916091432989

练习

1.在tools模块中定义 add函数,对两个数字进行求和计算
2.书写Testcase 代码对 add()进行测试
用例1: 1,2,3
用例2: 10,20,30
用例3: 2,3,5
  • 代码

    def add(a, b):
        return a+b
    
    # 用例代码
    import unittest
    
    from Test_lzy.Tools import add
    
    
    class TestAdd(unittest.TestCase):
    
        def test_method1(self):
            # 1,2,3
            if add(1, 2) == 3:
                print('测试通过')
            else:
                print('测试不通过')
    
        def test_method2(self):
            # 10,20,30
            if add(10, 20) == 30:
                print('测试通过')
            else:
                print('测试不通过')
    
        def test_method3(self):
            # 2,3,5
            if add(2, 3) == 5:
                print('测试通过')
            else:
                print('测试不通过')
    
    # 套件和执行的代码
    import unittest
    
    from Test_lzy.practice1 import TestAdd
    
    suite = unittest.TestSuite()
    
    suite.addTest(unittest.makeSuite(TestAdd))
    
    runner = unittest.TextTestRunner()
    runner.run(suite)
    

练习 - 对login函数的测试

  • 题目

    #假设对某网站的登录进行测试  不要写成登陆
    def login(username,password) :
    	if username == 'admin' and password == '123456' :
    	return '登录成功‘
    else:
    	return '登录失败'
    # 1.这个是开发书写的代码功能,不要修改我的 login函数
    # 2.可以认为这函数就是tpshop登录
    
    设计测试数据:
    正确的用户名和密码:admin,123456,登录成功
    错误的用户名: root,123456,登录失败
    错误的密码: admin,123123,登录失败
    错误的用户名和错误的密码: aaa,123123,登录失败
    
  • 代码

    import unittest
    
    from Test_lzy.Tools import login
    
    
    class TestLogin(unittest.TestCase):
        def test_username_password_ok(self):
            """正确的用户名和密码 admin 123456"""
            if login('admin', '123456') == '登录成功':
                print('pass')
            else:
                print('fail')
    
        def test_username_error(self):
            """不正确的用户名 root 123456"""
            if login('root', '123456') == '登录失败':
                print('pass')
            else:
                print('fail')
    
        def test_password_error(self):
            """不正确的密码 admin 123123"""
            if login('admin', '123123') == '登录失败':
                print('pass')
            else:
                print('fail')
    
        def test_username_password_error(self):
            """不正确的用户名和密码 aaa 123123"""
            if login('aaa', '123123') == '登录失败':
                print('pass')
            else:
                print('fail')
    

TestLoader(测试加载)

TestLoader(测试加载),作用和TestSuite 的作用是一样的,对TestSuite 功能的补充,用来组装测试用例的

比如:如果Testcase 的代码文件有很多,(10,20,30 )
  • 使用步骤

    1. 导包
    2. 实例化测试加载对象并添加用例 ---> 得到的是 suite 对象
    3. 实例化 运行对象
    4. 运行对象执行套件对象
  • 代码实现

    在一个项目中 Testcase(测试用例)的代码,一般放在一个单独的目录(case)
    
    """Testloader的使用"""
    # 1.导包
    import unittest
    
    # 2.实例化对象
    # unittest.TestLoader().discover('用例所在的路径','用例的代码文件名')
    # 用例所在的路径,建议使用相对路径,用例的代码文件名可以使用 * (任意多个任意字符)通配符
    
    suite = unittest.TestLoader().discover('./case', '*case*.py')
    
    # 3.实例化运行对象
    runner = unittest.TextTestRunner()
    
    # 4.执行
    runner.run(suite)
    
    # 可以将 3 4 步合为一步
    unittest.TextTestRunner().run(suite)
    
    #1.导包
    #2.使用默认的加载对象并加载用例
    #3.实例化运行对象并运行
    
    """TestLoader 的使用"""
    #1,导包
    import unittest
    
    # 2,使用默认的加载对象并加载用例
    suite = unittest.defaultTestLoader.discover( 'case ', '*case*.py ')
    
    # 可以将 3 4 步变为一步
    unittest.TextTestRunner( ).run(suite)
    

Fixture(测试夹具)

Fixture(测试夹具)是一种代码结构

在某些特定的情况下会自动执行

方法级别 [ 掌握 ]

  • 在每个测试方法(用例代码)执行前后都会自动调用的结构

    # 方法执行之前
    def setUp(self):
        每个测试方法执行之前都会执行
        pass
    
    # 方法执行之后
    def tearDown(self)
    	每个测试方法执行之后都会执行
        pass
    

类级别 [ 掌握 ]

  • 在每个测试类中所有方法执行前后都会自动调用的结构 (在整个类中 执行之前 执行之后 各一次 )

    # 类级别的Fixture方法,是一个 类方法
    # 类中所有方法之前
    @classmethod
    def setUpClass(cls):
        pass
    
    # 类中所有方法之后
    @classmethod
    def tearDownClass(cls):
        pass
    

模块级别 [ 了解 ]

模块:代码文件
在每个代码文件执行前后执行的代码结构
# 模块级别的需要写在类的外边直接定义函数即可
# 代码文件之前
def setUpModule():
    pass

# 代码文件之后
def tearDownModule():
    pass
  • 方法级别和类级别的前后的方法,不需要同时出现,根据用例代码的需要自行的选择使用

案例

1.打开浏览器(整个测试过程中就打开一次浏览器)类级别
2.输入网址(每个测试方法都需要一次)方法级别
3.输入用户名密码验证码点击登录(不同的测试数据)测试方法
4.关闭当前页面(每个测试方法都需要一次)方法级别
5.关闭浏览器(整个测试过程中就关闭一次浏览器)类级别
------
1.打开浏览器(整个测试过程中就打开一次浏览器)类级别
    
2.输入网址(每个测试方法都需要一次)方法级别
3.输入用户名密码验证码点击登录(不同的测试数据)测试方法
4.关闭当前页面(每个测试方法都需要一次)方法级别
2.输入网址(每个测试方法都需要一次)方法级别
3.输入用户名密码验证码点击登录(不同的测试数据)测试方法
4.关闭当前页面(每个测试方法都需要一次)方法级别
2.输入网址(每个测试方法都需要一次)方法级别
3.输入用户名密码验证码点击登录(不同的测试数据)测试方法
4.关闭当前页面(每个测试方法都需要一次)方法级别
                
5.关闭浏览器(整个测试过程中就关闭一次浏览器)类级别
  • 代码

    import unittest
    
    
    class TestLogin(unittest.TestCase):
        def setUp(self):
            """每个测试方法执行之前都会先调用的方法"""
            print('输入网址')
    
        def tearDown(self):
            """每个测试方法执行之后都会调用的方法"""
            print('关闭当前页面')
    
        @classmethod
        def setUpClass(cls) -> None:
            print('-----1.打开浏览器')
    
        @classmethod
        def tearDownClass(cls) -> None:
            print('-----5.关闭浏览器')
    
        def test_1(self):
            print('输入正确用户名密码验证码,点击登录 1')
    
        def test_2(self):
            print('输入错误用户名密码验证码,点击登录 2')
    
    

断言

  • 让程序代替人工自动的判断预期结果和实际结果是否相符

    让程序代替人工自动的判断预期结果和实际结果是否相符.
    
    断言的结果有两种:
    >True,用例通过
    > False,代码抛出异常,用例不通过
    
    在unittest中使用断言,都需要通过self.断言方法来试验
    

assertEqual

self.assertEqual(预期结果,实际结果)  #判断预期结果和实际结果是否相等
1.如果相等,用例通过
2.如果不相等,用例不通过,抛出异常

assertIn

self.assertIn(预期结果,实际结果)#判断预期结果是否包含在实际结果中
1.包含,用例通过
2.不包含,用例不通过,抛出异常

assertIn( ' admin' , 'admin' )   # 包含
assertIn( ' admin', 'adminnnnnnnn ')  # 包含
assertIn( ' admin' , 'aaaaaadmin' )  # 包含
assertIn( ' admin', 'aaaaaadminnnnnnn')   # 包含assertIn( 'admin' , 'addddddmin' )  #不是包含

参数化

参数化在测试方法中,使用 变量 来代替具体的测试数据,然后使用传参的方法将测试数据传递给方法的变量
好处:相似的代码不需要多次书写.

工作中场景:
1.测试数据一般放在json文件中
2.使用代码读取json文件,提取我们想要的数据--->[(),()] or [[],[]]

安装插件

  • unittest 框架本身是 不支持 参数化,想要使用参数化,需要安装插件来完成

    - 联网安装(在 cmd 窗口安装 或者 在 pycharm 的终端窗口)
    pip install parameterized
    
  • pip

    • pip 是 Python 中包 ( 插件)的管理工具,使用这个工具下载安装插件
  • 验证

    pip list  # 查看到parameterized
    

    image-20220918151836476

参数化代码

  • 1.导包unittest/ pa

    2.定义测试类

    3.书写测试方法(用到的测试数据使用变量代替)

    4.组织测试数据并传参

image-20220918152924532

  • 代码

    # 1.导包unittest/ pa
    import unittest
    from parameterized import parameterized
    
    from Test_lzy.Tools import login
    
    # 组织测试数据并传参 [(),(),()] or [[],[],[]]
    data = {
        ('admin', '123456', '登录成功'),
        ('root', '123456', '登录失败'),
        ('admin', '123123', '登录失败'),
        ('aaa', '123123', '登录失败')
    }
    
    # 2.定义测试类
    class TestLogin(unittest.TestCase):
    
        # 3.书写测试方法(用到的测试数据使用变量代替)
        @parameterized.expand(data)
        def test_login(self, username, password, expect):
            self.assertEqual(expect, login(username, password))
    
    # 4.组织测试数据并传参 (装饰器 @)
    

    image-20220918180327356

跳过

  • 对于一些未完成的或者不满足测试条件的测试函数和测试类,不想执行,可以使用跳过使用方法,装饰器完成
  • 代码书写在 TestCase 文件
# 直接将测试函数标记成跳过
@unittest.skip('跳过的原因')
# 根据条件判断测试函数是否跳过 , 判断条件成立,跳过
@unittest.skipIf(判断条件,'跳过原因')
  • 代码

    import unittest
    
    version = 30
    
    
    class TestDemo(unittest.TestCase):
        @unittest.skip('不想执行')
        def test_1(self):
            print('测试方法 1')
    
        @unittest.skipIf(version >= 30, '版本大于等于30,不用测试')
        def test_2(self):
            print('测试方法 2')
    
        def test_3(self):
            print('测试方法 3')
    
    

测试报告

自带的测试报告

  • 只有单独运行TestCase 的代码,才会生成测试报告

生成第三方的测试报告

  1. 获取第三方的 测试运行类模块,将其放在代码的目录中
  2. 导包unittest
  3. 使用套件对象,加载对象去添加用例方法
  4. 实例化第三方的运行对象并运行套件对象
  • 代码

    # 1. 获取第三方的  测试运行类模块,将其放在代码的目录中
    # 2. 导包unittest
    import unittest
    from HTMLTestRunner import HTMLTestRunner
    
    # 3. 使用套件对象,加载对象去添加用例方法
    suite = unittest.defaultTestLoader.discover('.', 'practice1.py')
    # 4. 实例化第三方的运行对象并运行套件对象
    HTMLTestRunner()
    # stream=sys.stdout, 必填, 测试报告的文件对象(open ),注意点,要使用wb打开
    # verbosity=1,  可选,报告的详细程度,默认1简略,2详细
    # title=None,  可选,测试报告的标题
    # description = None  可选,描述信息,Python 的版本,pycharm版本
    
    file = 'report2.html'  # 报告的后缀是html
    with open(file, 'wb')as f:
        runner = HTMLTestRunner(f, 2, '测试报告', 'python 3.6.8') # 运行对象
    
        # 运行对象执行套件,要写在with的缩进中
        runner.run(suite)
    
  • 流程

    1.组织用例文件(Testcase里边),书写参数化,书写断言(判断预期&实际),书写Fixture,书写跳过...如果单个测试测试文件,直接运行,得到测试报告; 如果有多个测试文件,需要组装运行生成测试报告
    
    2.使用套件对象组装,或者使用加载对象组装
    
    3.运行对象运行 runner.run
    3.1 运行对象 = 第三方的运行类(文件对象(打开文件需要使用wb方式 -- 二进制写, 写了wb就不要再写encoding了))
    3.2 运行对象.run(套件对象)
    
    

image-20220918192018572

标签:__,代码,Python,unittest,面向对象,对象,print,self,def
From: https://www.cnblogs.com/lzy5967/p/16705537.html

相关文章

  • python实训2
    test3-1print("今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问几何?\n")number=int(input("请输入您认为符合条件的数:"))ifnumber%3==2andnumber%5==3andnumb......
  • 【利用Python进行edf格式的肌电处理的一些过程记录】-2022/09
    (1)获取原肌电数据,生成图片1.使用的库:mne2.肌电数据的格式:edfdefShow_Information(file):raw=read_raw_edf(file,preload=True)info=raw.info......
  • python实验报告(第三周)
    实验3:流程控制语句一、实验目的和要求1、了解Python的程序结构形式和嵌套语句的格式;2、掌握if、if…else、if…elif…else选择语句的使用;3、掌握条件表达式和while、fo......
  • 编译原理:python编译器--运行时机制
    python的运行时机制的核心--python对象机制的设计理解字节码的执行过程用GDB跟踪执行一个简单的示例程序,它只有一行:“a=1”。对应的字节码如下。其中,前两行指令实现......
  • 力扣206(java&python)-反转链表(简单)
    题目:给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。示例1:输入:head=[1,2,3,4,5]输出:[5,4,3,2,1]示例2:输入:head=[1,2]输出:[2,1]示例3:输入:head=......
  • python自动发送邮件
    需要什么库?PyEmail这个库能够实现基于SMTP协议自动发送邮件的功能参考:PythonSMTP发送邮件|菜鸟教程(runoob.com)需要哪些准备?一个开启SMTP服务的邮箱这里以163网......
  • 学习python-Day62
    今日学习内容具体项目:D:\pythonProject\django_day60登录界面搭建<divclass="container-fluid"><divclass="row"><divclass="col-md-6col-md-offse......
  • Python函数-2
    一.导入$$f(x,y)=2x+3y$$上面括号里面的就是数学公式里的自变量,自变量就相当于函数里的参数。二.为什么要有参数如果一个大楼里有两种尺寸不一的窗户,显然在......
  • 修改-Python函数-2
    一.导入$$f(x,y)=2x+3y$$上面括号里面的就是数学公式里的自变量,自变量就相当于函数里的参数。二.为什么要有参数如果一个大楼里有两种尺寸不一的窗户,显然在......
  • 配置绿色免安装版的C/C++、java、python环境,重装系统可以继续使用
    windows系统用一段时间就会变得臃肿,我平均每年都会重装一下系统,因此需要配置免安装的环境,重装系统可以继续使用。 1、C/C++环境安装配置主流都是用minGwin的gcc/g++,最......