IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> 自动化测试框架之Unittest -> 正文阅读

[开发测试]自动化测试框架之Unittest

自动化测试之单元测试

单元测试:

通常而言,一个单元测试用例是用于判断某个特定条件或场景下某个特定函数的行为

直观描述:针对一个函数,构造不同输入,验证函数的输出是否符合预期

但需要注意:输入并非都是显性输入,存在隐性输入(读取到的文件或数据库数据)

单元测试的意义:

从质量角度:

  • 测试针对性强,bug更容易暴露
  • 场景构造简单,核心功能验证更充分
  • 保证代码结构良好,具有较高的可测性和可维护性

从效率角度:

  • 测试场景构建快捷,减少调试时间
  • 只针对修改的代码展开测试,减少测试时间
  • 更多bug在项目早期被发现,缩短开发周期

Unitest基本概念

  1. test 测试用例? :针对一个特定场景,特定目的的具体测试用例
  2. testcase? 测试类? :可以包含同一个测试对象的多个测试用例(比如一个函数的不同输入)
  3. testsuite? 测试集? :可以包含多个测试类的多个测试用例
  4. Assertion? 断言? ?:必须使用断言判断测试结果
  5. testfixture:为测试做统一的初始化和清除工作
  • setUp? ?在测试类的每个测试用例执行前执行
  • tearDown? 在测试类的每个测试用例执行后执行
  • setUpClass? 在测试类的第一个测试用例执行前执行
  • tearDownClass 在测试类的最后一个测试用例执行后执行

执行顺序示意如下:(注意两两不需要配对出现

setUpClass
????????setUp
????????????????测试用例1
????????tearDown
????????setUp
????????????????测试用例2
????????tearDown
????????……
tearDownClass

示例1:?

#! person.py

class Person(object):

    def __init__(self,name):
        self.name=name

    def get_name(self):
        return self.name
#!persontest.py

import unittest
import person

class PersonTestCase(unittest.TestCase):
    
    def setUp(self):
        #初始化
        self.p1=person.Person('zhang')
        self.p2=person.Person('li')

    def test_get_name(self)#必须test开头才会被认为是测试用例
        self.assertEqual(self.p1.get_name(),'zhang') #断言判断是否一致
        self.assertEqual(self.p1.get_name(),'li')

示例2:

#! company.py

class Company(object):

    def __init__(self,name,boss):
        self.name=name
        self.boss=boss
        self.staffs=set()

    def who is boss(self):
        return boss.get_name()

    def hire(self,person)#雇佣新员工
        '''
            ……
        '''
    
    def fire(self,person)#解雇员工
        '''
            ……
        '''
#! companytest.py

import company
import person
import unittest

class CompanyTestCase(unittest.TestCase):
    @classmethod #必须使用类class装饰器才能使用 setUpClass  
    def setUpClass(self):
        self.boss=person.Person('wang')
        self.acg=company.Company('ACG',self.boss)
    
    def tearDown(self):
        self.acg.staffs.clear()

    def test_who_is_boss(self):#测试用例 老板姓名是否一致
        self.assertEqual(self.acg.who_is_boss(),'wang')

    def test_number_of_staffs_increase_when_hire(self):#测试用例 每雇佣一个人员工数是否加一
        self.acg.hire(person.Person('li'))
        self.assertEqual(len(self.acg.staffs),1)
        self.acg.hire(person.Person('zhang'))
        self.assertEqual(len(self.acg.staffs),2)

    def test_number_of_staffs_notchange_when_hire(self):#测试用例 雇佣相同一个人员工数是否增加
        self.acg.hire(person.Person('li'))
        self.assertEqual(len(self.acg.staffs),1)
        self.acg.hire(person.Person('li'))
        self.assertEqual(len(self.acg.staffs),1)

运行Unittest

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

执行输出

·(点)测试正确(测试通过)
F测试失败
E测试异常(给出断言错误)

运行测试集

testsuite? 测试集? :可以包含多个测试类的多个测试用例

def MyTestSuite():

    suite=unittest.TestSuite()

    #只针对部分测试类的其中一个测试用例进行测试
    suite.addTest(PersonTestCase('test_get_name'))
    suite.addTest(CompanyTestCase('test_who_is_boss'))

    return suite

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

单元测试规范

  • 必须使用断言判断结果 unittest.TestCase.assert
    assertEqual? /? assertNotEqual判断相等或不等
    assertTrue? / assertFalse判断boolean(True? False)
    assertRaises判断抛出异常是否符合预期
  • 测试用例需要有自表述能力 ,达到见名知意
  • 测试用例之间相互独立,不应相互依赖,相互调用
  • 一个测试用例只测一个函数?
  • 代码需要保持一致性(非必要不引入 time.time()和random.random())

统计单元测试覆盖率

coverge工具

  • 生成覆盖率统计文件.coverge

替换python test.py arg1 arg2coverge run --branch test.py arg1 arg2

  • 移除部分不需要统计覆盖率文件

coverge report -m --omit=file1,file2

  • 打印覆盖率信息

coverge report -m 获取当前目录下.coverge文件

Stmts总行数
Miss未覆盖行数
Branch总分支行数
BrPart未覆盖分支数
Cover覆盖率
Missing未覆盖具体信息(未覆盖行号信息)

进一步信息可视化(直观便捷的HTML报告)

coverge html --omit=file1,file2

Mock工具

为什么要使用Mock?

  1. 要测模块A,但它要调用的模块B还未开发完成
  2. 代码中有结果不可预知的代码(随机数 当前时间)
  3. 其他模块的质量不可靠

Mock目的:

  • 构造模块
  • 改变函数逻辑
  • 减少依赖

常见场景:

  • 场景1:永远返回我们想要的返回值?

关键字:return_value

支持python常见数据类型

#! mockdemo01.py

import company
import person
import mock

boss=person.Person('zhang')
shoeshop=company.Company('shoe shop',boss)

boss.get_name=mock.Mock(return_value='li')
print shoeshop.who_is_boss()

boss.get_name=mock.Mock(return_value=12345)
print shoeshop.who_is_boss()

boss.get_name=mock.Mock(return_value=[1,2,'4',5])
print shoeshop.who_is_boss()
  • 场景2:根据调用次数返回想要的结果? 每次调用测试返回结果都可以不一致

关键字:side_effect

超出调用次数抛StopIteration异常

#! mockdemo02.py

import company
import person
import mock

boss=person.Person('zhang')
shoeshop=company.Company('shoe shop',boss)

boss.get_name=mock.Mock(side_effect=['li',12345])

print shoeshop.who_is_boss() #li

print shoeshop.who_is_boss() #12345

print shoeshop.who_is_boss() #StopIteration异常  超过设定调用次数
  • 场景3 根据参数返回想要的结果

关键字:side_effect

替换函数逻辑

#! mockdemo03.py

import company
import person
import mock

boss=person.Person('zhang')
shoeshop=company.Company('shoe shop',boss)

def new_logic(arg):
    pairs={'a':123,
        True:'abc'
        }
    return pairs[arg]

boss.get_name=mock.Mock(side_effect=new_logic)

print boss.get_name(True) #abc
print boss.get_name('a')  #123
  • 场景4?抛出想要的异常或错误

关键字:side_effect

#! mockdemo04.py

import company
import person
import mock

boss=person.Person('zhang')
shoeshop=company.Company('shoe shop',boss)

boss.get_name=mock.Mock(side_effect=KeyError('keyerror'))

print shoeshop.who_is_boss() #抛出异常 keyerror
  • 场景5?限制mock作用域

关键字:with? patch.object

#! mockdemo05.py

import company
import person
import mock

boss=person.Person('zhang')
shoeshop=company.Company('shoe shop',boss)

print shoeshop.who_is_boss() #zhang

with mock.patch.object(person.Person,'get_name',return_value='li'):
    print shoeshop.who_is_boss() #li

print shoeshop.who_is_boss() #zhang
  • 场景6 获取调用信息
called函数是否被调用(Boolean)
call_count函数被调用次数
call_args函数被调用最后一次参数
call_args_list函数被调用参数列表
#! mockdemo06.py

import company
import person
import mock

boss=person.Person('zhang')
shoeshop=company.Company('shoe shop',boss)

boss.get_name=mock.Mock(side_effect=boss.get_name)

print boss.get_name.called  #False
print boss.get_name.call_count  #0
print boss.get_name.call_args  #None
print boss.get_name.call_args_list #[]

print shoeshop.who_is_boss()
print shoeshop.who_is_boss()
print shoeshop.who_is_boss()

print boss.get_name.called  #True
print boss.get_name.call_count  #3
print boss.get_name.call_args  #call()
print boss.get_name.call_args_list #[call(),call(),call()]
  • 场景7 在返回值改变的同时,确保API不会因为mock改变

关键字:create_autospec

#! mockdemo07.py

import company
import person
import mock

boss=person.Person('zhang')
shoeshop=company.Company('shoe shop',boss)

shoeshop.who_is_boss=mock.create_autospec(shoeshop.who_is_boss,return_value='li')

print shoeshop.who_is_boss() #li

print shoeshop.who_is_boss('abc') #抛出异常
  • 场景8 从零构造依赖模块

适用于依赖模块只定义了接口,但尚未开发场景

#! mockdemo08.py

import company

import mock

#company模块中调用的person模块尚未开发 需要构造
boss=mock.Mock()
boss.get_name=mock.Mock(return_value='zhang')

shoeshop=company.Company('shoe shop',boss)

print shoeshop.who_is_boss() #zhang
  • 场景9 替换函数调用链

函数调用链:os.popen(cmd).read().split()

Mock方法:

os.popen=mock.Mock()

os.popen.return_value.read.return value.split.return_value=xx

#! mockdemo09.py

import os
import mock

print os.popen('hostname').read().split() #['host']

os.popen=mock.Mock()

os.popen.return_value.read.return value.split.return_value='mock'

print os.popen('hostname').read().split() #mock
  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2022-01-12 00:19:40  更:2022-01-12 00:21:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/18 5:35:43-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码