天天看点

c语言ut测试白盒测试方法,白盒测试

测试理论回顾:

黑盒测试:是针对整个产品系统进行的测试,验证系统是否满足需求规格的定义,及软件产品的正确性和性能等是否满足其需求规格的要求。

灰盒测试:是介于白盒测试与黑盒测试之间的一种测试,灰盒测试多用于集成测试阶段,不仅关注输出、输入的正确性,同时也关注程序内部的情况。

白盒测试:是通过程序的源代码进行测试而不使用用户界面。这种类型的测试需要从代码内部在算法,路径,条件等等中的缺点或者错误,进而加以修正。

单元测试:

单元测试是针对程序的最小单元来进行正确性检验的过程。单元:一个单元可能是单个程序、类、对象、方法(函数)等。 优点是可以减少BUG;快速定位BUG;提高代码质量;减少调试时间。缺点是周期时间长;耗费资源(主要是人力资源);能力要求高。

一、单元测试流程

1. 单元测试-计划:1) 确定要测试代码范围;2) 评估标准(确定被测代码的覆盖率)

2. 测试策略-设计:1) 拿到开发代码进行调整(保证单元间可独立执行)

3. 测试策略-实现:1) 根据调整好的代码-画流程图;2) 根据流程图画流图-确定复杂度、路径;3) 根据复杂度和路径确定测试用例(测试数据)

4. 单元测试-执行:1) 使用测试框架(UnitTest)编写单元测试用例;2) 测试用例(代码)和测试数据分离;3) 生成测试报告

二、单元测试-计划

确定要测试代码以及确定这些被测代码的评估标准、优先级等说明:1. 确定单元测试范围(哪些代码要做单元测试);评估标准-(被测代码的逻辑覆盖率)

1.1、如何确定20%代码

确定单元测试的代码测试范围,我们依据:二八原则(20%的代码中隐藏了80%的缺陷)

1. 频率:使用频率高的代码段;

2. 复用性:(是否已被复用,是否被别的地方引用,如果被别的地方调用这个函数,没问题,基本不用测)

1). 全新(指没有被复用的代码)

2). 部分复用

3). 全部复用

3. 开发人员:

1). 技术(指由技术水平不那么高的开发人员写的代码)

2). 业务(指由对业务不那么熟悉的开发人员写的代码)

4. 复杂度:业务逻辑复杂度(一般认为圈复杂度在7级以上代码包括在20%的代码中)

最后,我们会得到一张表,来列名我们要测的代码范围:

c语言ut测试白盒测试方法,白盒测试

测试范围

1.2、评估标准(覆盖率)如何确定逻辑覆盖率

例如,我们拿到一段代码,实现的一个需求,我们首先要画流程图(使用统一规定标准图形,描述程序运行的具体步骤),然后以此来确定覆盖率,并且以后可以根据流程图画流图    1. 语句覆盖率,说明:非分支非判断的代码,覆盖率计算方式:被覆盖语句/总语句,例如,流程图里有3个语句,对于一个测试数据,它能覆盖2个语句,则语句覆盖率为2/3

2. 分支覆盖率,说明:判断语句的分支,例如if判断有两个分支,覆盖率计算方式:覆盖分支/总分支数,例如流程图里的if判断有2个分支,对于一个测试数据,它能覆盖1个分支,则分支覆盖率为1/2

3. 条件覆盖率,说明:一个条件有两个结果 true、false,所以一个条件分母为2,两个条件分母为4...例如:if username=='admin' and pwd=='123456':这里就有两个条件,分母为4

注意:条件之间使用逻辑符 and连接,第一个条件如果失败,不会在去判断第二个条件,如果测试数据是username==ad, pwd就不会判断,覆盖率为1/4;如果为or第一个条件失败回去判断第二条件

4. 路径覆盖率,说明:路径就是从开始-到结束的过程, 覆盖率计算方式:覆盖路径/全部路径

注意:路径的分子永远为1, 有时候看流程图路径有4条,但有一条永远不可成立,所以实际路径为3条,计算路径覆盖率时分母就为3

5. 分支条件覆盖率,说明:分支和条件的组合,1.分子=分支覆盖率的分子+条件覆盖率的分子;2.分母=分支覆盖率的分母+条件覆盖率的分母

三、单元测试策略-设计

单元测试策略:针对单元测试选择测试的一种方式;

单元测试策略方式:

1. 自上到下,方式:从最上层函数往下开始逐层测试,缺点(成本高)

2. 自下到上,方式:从最底层函数往上开始逐层测试,缺点(周期长,需要开发写完所有代码才能开始测试)

3. 孤立策略【推荐使用】方式:选择需要进行测试的函数进行测试,优点:选择重要的代码进行测试,测试构成中免不了测的某些函数会调用别的函数,所以一定要学会打桩

注意:打桩-->打桩就是模拟编写一个我们需要引用的函数,模拟定义被调用的函数名,提示:一般我们只模拟写个函数名,直接返回相应的结果即可(return 结果;pass)

示例:def fun_1(self):

return true

四、单元测试策略-实现

测试策略实现:把我们选定的代码,而且保证选定的代码能独立运行(已经打完桩),转向流程图、流图及用例的过程

测试策略实现如何操作:1. 将测试代码转换成流程图;2. 根据流程图转换为流图,有了流图测试用例就出来了

c语言ut测试白盒测试方法,白盒测试

流程图

什么是流图?

概念:表达程序业务逻辑的复杂度一种示意图

构成:

1) 圈:判断条件(一个条件一个圈)、语句块(一条或多条语句,挨着的语句可以用一个圈表示)两者都圈

2) 线:箭头指向的线,用来连接圈和圈的指向

流图的目的:

1) 确定单元的复杂度级别

2) 确定测试用例

备注: 路径的个数为复杂度的级别(一条路径为1个复杂度级别),有N个条件最少有N条路径,最多N+1条路径

c语言ut测试白盒测试方法,白盒测试

流图

* 路径:2 (1-2-3,1-3);复杂度:2;用例个数:2

c语言ut测试白盒测试方法,白盒测试

根据流图写的测试用例

五、单元测试——执行

通过单元测试框架对要进行测试代码的实践过程需求:通过Python语言编写一个运算的类(Calc),类中包含两个函数:add(self,a,b) 返回a+b之和;sub(self,a,c) 返回a-c之差

- 导包 UnitTest 、Calc类

- 新建单元测试类 Test01(集成 unitTest.TestCase)

- 新建testAdd()函数

- 导入Calc类中的add函数

- 添加断言

- 新建testSub()函数

- 导入Calc类中的sub函数

- 添加断言

- 执行测试:unittest.main()import unittest

# 导入要测试的 Calc类

from UT.Day02.Code.test01_lx01import Calc

class Test_Calc(unittest.TestCase):

def setUp(self):

print("setUp被执行")

def tearDown(self):

print("tearDown被执行")

def test_add(self):

result=Calc().add(10,10)

print("test_add方法被执行")

self.assertEqual(result,20)

def test_sub(self):

result=Calc().sub(10,20)

print("test_sub方法被执行")

self.assertEqual(result,-10)

if __name__== '__main__':

# main运行当前类中所有test开头的方法

unittest.main()

数据直接写入代码中,如果数量庞大,我们需要频繁改动数据或复制冗余代码进行实现数据测试目的。做不到数据分离(代码和数据分开),不便维护

参数化

根据需求动态获取数据并进行赋值的过程

参数化方式:XML格式(1); CSV格式(2);JSON串 (3);TXT文本(4)

提示:括号内为推荐使用优先级,从小到大;1-2为扩展了解,3-4建议重点了解

XML格式:

XML是一种标记语句,很类似HTML标记语言;后缀 .xml;XML是传输和存储数据,焦点在数据;HTML是显示数据,焦点在外观;XML不适合大量参数化数据时使用XML格式:

单元测试

XST

2008

39.95

1. 必须有XML声明语句:<?xml version="1.0" encoding="UTF-8"?>

2. 必须要有一个根元素,如:

3. 标签大小写敏感

4. 属性值用双引号

5. 标签成对

6. 元素正确嵌套

7. 标签名可随意命名,但有以下限制(注意:命名允许重复)

1) 不能以数字或者标点符号开始参

2)不能以字符 “xml”(或者 XML、Xml)开始

3) 名称不能包含空格需求:对三角形案例单元测试使用XML格式进行参数化

被测代码段:

class Sjx(): def sjx(self,a,b,c):

# 判断是否为三角形

if a+b>c and a+c>b and b+c>a:

# 判断是否为等边三角形

if a==b and b==c:

return "等边三角形"

elif a==b or a==c or b==c:

return "等腰三角形"

else:

return "普通三角形"

else:

return "不是三角形"

if __name__ == '__main__':

print(Sjx().sjx(2,3,4))

--------------------------------------------------------------------------------------------------------------------------

操作步骤:1. 编写XML数据文件;2. 编写读取XML模块函数;3. 单元测试模块中引用XML读取函数;4. 执行

编写XML数据文件:

2

3

4

普通三角形

2

2

2

等边三角形

2

2

3

等腰三角形

2

3

2

等腰三角形

3

2

2

等腰三角形

3

2

1

不是三角形

1

2

3

不是三角形

2

3

1

不是三角形

编写读取XML模块函数# 导包

minidomfrom xml.dom import minidom

class Read_Xml():

def readXml(self,node,num,nodeChild):

# 解析xml文档

dom=minidom.parse("../DataPool/sjx.xml")

# 获取文档对象

root=dom.documentElement

# 获取bian元素

element=root.getElementsByTagName(node)[int(num)]

# 获取指定bian元素 如b1

return element.getElementsByTagName(nodeChild)[0].firstChild.data

def get_len(self,node):

# 解析xml文档

dom=minidom.parse("../DataPool/sjx.xml")

# 获取文档对象

root=dom.documentElement

# 获取bian元素

return len(root.getElementsByTagName(node))

if __name__ == '__main__':

print(Read_Xml().readXml("bian",0,"b3"))

print(Read_Xml().get_len("bian"))

单元测试模块中引用XML读取函数;执行# 导包 unittest、三角形函数、读取xml数据类

import unittest

from UT.Day02.Code.sjx import Sjx

from UT.Day02.ReadData.read_xml import Read_Xml

# 实例化三角形

sjxClass=Sjx()

# 实例化读取xml类

readXmlClass=Read_Xml()

class Test_Xml(unittest.TestCase):

def test001(self):

for i in range(readXmlClass.get_len("bian")):

# 目的测试三角形程序

result=sjxClass.sjx(int(readXmlClass.readXml("bian",i,"b1")),                                             int(readXmlClass.readXml("bian", i, "b2")),                                                                                int(readXmlClass.readXml("bian", i, "b3")))

# 添加断言,判断三角形程序返回的结果是否符合我们预期结果             self.assertEqual(result,readXmlClass.readXml("bian", i, "expect"))             print(readXmlClass.readXml("bian",i,"b1"), readXmlClass.readXml("bian", i, "b2"),                     readXmlClass.readXml("bian", i, "b3"), readXmlClass.readXml("bian", i,                     "expect"),"--》验证通过")

重点分析:

1. 导入XML包 from xml.dom import minidom

2. 加载解析 dom=minidom.parse(filename)

3. 获取对象  root=dom.documentElement

4. 获取子元素 aas=root.getElementsByTagName(one)[0]

5. 获取子元素值 aas.getElementsByTagName(two)[0].firstChild.data

CSV格式:

CSV是一种以逗号做分割的表格格式; 后缀 .csv使用CSV实现三角形案例参数化-操作步骤

1. 创建CSV文件

2. 编写CSV读取模块函数

3. 单元测试-引用CSV读取函数

4. 执行

创建CSV文件:

2,2,2,等边三角形2,2,3,等腰三角形2,3,2,等腰三角形3,2,2,等腰三角形1,2,3,不是三角形2,3,1,不是三角形3,2,1,不是三角形2,3,4,普通三角形

编写CSV读取模块函数:

# 导包

import csv

class Read_Csv():

def readCsv(self):

# 打开csv

with open("../DataPool/sjx.csv","r",encoding='utf-8') as f:

datas=csv.reader(f)

# 新建空列表,把单行返回的列表添加成整体的列表

lines=[]

for data in datas:

# 添加数组

lines.append(data)

return lines

if __name__ == '__main__':

print(Read_Csv().readCsv())

单元测试-引用CSV读取函数;执行:

# 导入unittest、三角形函数、csv读取参数类

import unittest

from UT.Day02.Code.sjx import Sjx

from UT.Day02.ReadData.read_csv import Read_Csv

# 实例化三角形

sjxClass=Sjx()

# 实例化读取csv工具

readCsvClass=Read_Csv()

class Test_Csv(unittest.TestCase):

# 测试三角形函数程序

def test001(self):

for i in range(len(readCsvClass.readCsv())):             result=sjxClass.sjx(int(readCsvClass.readCsv()[i][0]),

int(readCsvClass.readCsv()[i][1]),

int(readCsvClass.readCsv()[i][2]))

# 断言:三角新运行完后返回的结果和预期结果做对比             self.assertEqual(result,readCsvClass.readCsv()[i][3])

print(readCsvClass.readCsv()[i][0], readCsvClass.readCsv()[i][1],                     readCsvClass.readCsv()[i][2], readCsvClass.readCsv()[i][3],"---》验证通过")

if __name__ == '__main__':

unittest.main()

重点分析:

1. 导包 import csv

2. 打开csv文件:

with open("../Data/sjx.csv","r",encoding="utf-8") as f:

lines=csv.reader(f)

JSON串:

一种轻量级数据交换格式;后缀名 .json ;提示: 接口测试一般使用JSON为接口传递数据规范格式

格式:{"name":"张三","age":28};提示:由键值对组成,健名和值之间使用分号(:)分割,多个键值对之间使用逗号(,)分割使用JSON实现三角形案例参数化-操作步骤

1. 编写JSON文件

2. 编写JSON读取模块函数

3. 单元测试-引用JSON读取函数

4. 执行

编写JSON文件 :

{"data": [ {"b1":2,"b2":2,"b3":2,"expect":"等边三角形"}, {"b1":2,"b2":2,"b3":3,"expect":"等腰三角形"}, {"b1":2,"b2":3,"b3":2,"expect":"等腰三角形"} ]}

编写JSON读取模块函数 :# 导包 json

import json

# 打开文件流

class Read_Json():

def readJson(self):

with open("../DataPool/sjx.json","r",encoding="utf-8") as f:

# 调用load()

datas=json.load(f)

# 返回字典data键名对应的值

return datas["data"]

if __name__ == '__main__':

print(Read_Json().readJson())

单元测试-引用JSON读取函数;执行 :# 导包 unittest 、三角形函数程序、读取json类

import unittest

from UT.Day02.Code.sjx import Sjx

from UT.Day02.ReadData.read_json import Read_Json

# 实例化三角形

sjxClass=Sjx()

# 实例化读取JSON类

readJsonClass=Read_Json()

class Test_Json(unittest.TestCase):

# 测试三角形

def test001(self):

for i in range(len(readJsonClass.readJson())):

# 调用三角形函数

result=sjxClass.sjx(int(readJsonClass.readJson()[i]["b1"]),                                                                                       int(readJsonClass.readJson()[i]["b2"]),                                                                                       int(readJsonClass.readJson()[i]["b3"]))

# 断言 三角形返回的结果是否与预期结果相符             self.assertEqual(result,readJsonClass.readJson()[i]["expect"])

# 打印运行参数及结果

print(readJsonClass.readJson()[i]["b1"], readJsonClass.readJson()[i]["b2"],                     readJsonClass.readJson()[i]["b3"], readJsonClass.readJson()[i]["expect"],"--->                        通过!")

if __name__ == '__main__':

unittest.main()

难点分析

1. 导入JSON包(import JSON)

2. 打开JSON文件并解析

with open('../DataXML/sjx.json','r',encoding='utf-8') as f:

file=json.load(f)

TXT文本:

一种纯文本格式; 后缀名 .txt;TXT文本优点: 编写测试数据方便;使用模块函数读取时便捷使用TXT实现三角形案例参数化-操作步骤

1. 创建txt文本并写入测试数据

2. 编写读取txt模块函数

3. 单元测试-引用JSON读取函数

4. 执行

创建txt文本并写入测试数据:第一条边,第二条边,第三条边,预期结果

2,2,2,等边三角形

2,2,3,等腰三角形

2,3,2,等腰三角形

3,2,2,等腰三角形

1,2,3,不是三角形

2,3,1,不是三角形

3,2,1,不是三角形

2,3,4,普通三角形

编写读取txt模块函数 :class Read_Txt():

def readTxt(self):

# 打开txt文件流

with open("../DataPool/sjx.txt","r",encoding="utf-8") as f:

# 通过文件流调用读取的方法读取数据--->所有行

datas=f.readlines()

# 新建列表 --》添加分割后的单行列表数据

lines=[]

# 遍历

for data in datas:

'''

strip():去除字符串前后回车,空格,tab键

split():使用指定符号进行分割字符串并以列表格式返回分割后的数据

'''

# 把分割后的单行列表数据添加到整体的列中,并返回

lines.append(data.strip().split(","))

# 返回数据 [1:] 返回从下标1开始返回数据

return lines[1:]

'''

f.read() #读取所有数据

f.readline() # 读取单行

f.readlines() # 读取所有行

'''

if __name__ == '__main__':

print(Read_Txt().readTxt())

单元测试-引用JSON读取函数;执行 :# 导包 unittest、三角形、txt读取

import unittest

from UT.Day02.Code.sjx import Sjx

from UT.Day02.ReadData.read_txt import Read_Txt

# 实例化三角形

sjxClass=Sjx()

# 实例化txt

readTxtClass=Read_Txt()

class Test_Txt(unittest.TestCase):

# 测试三角形程序

def test001(self):

for i in range(len(readTxtClass.readTxt())):

# 调用三角形方法

result=sjxClass.sjx(int(readTxtClass.readTxt()[i][0]),

int(readTxtClass.readTxt()[i][1]),

int(readTxtClass.readTxt()[i][2]))

# 调用断言,判断三角形程序执行后的结果是否与预期结果相符

self.assertEqual(result,readTxtClass.readTxt()[i][3])

# 为了查看方便,我把执行成功后的数据打印一下

print(readTxtClass.readTxt()[i][0], readTxtClass.readTxt()[i][1],                     readTxtClass.readTxt()[i][2], readTxtClass.readTxt()[i][3],"-->通过!")

if __name__ == '__main__':

unittest.main()

难点分析:导包:不需要读取方法:readlines()

1. 如何读取txt文本?

with open(r'../DataXML/三角形.txt','r',encoding='utf-8') as f:

2. 如何去除行尾/n换行符?

line.strip()

c语言ut测试白盒测试方法,白盒测试

单元测试总结

--------------------------------------------------------------------------------

HTML报告生成

如何生成HTML报告?导入HTML报告模板模板:HTMLTestRunner.py编写生成HTML模块

# 导入unittest包

import unittest

# 导入 HTMLTestRunner模板包

from UnitTest.Day02.ReadData.HTMLTestRunner import HTMLTestRunner

#导入时间包

import time

# 定义测试模块路径

dirpath='.'

discorver=unittest.defaultTestLoader.discover(dirpath,pattern='test*.py')

if __name__=='__main__':

#存放报告的文件夹

report_dir='../TestReport'

#报告名称含时间,时间格式

now=time.strftime("%Y-%m-%d %H_%M_%S")

#报告完整路径+名称

report_name=report_dir+'/'+now+'result.html'

#打开报告写入结果

with open(report_name,'wb')as f:

runner=HTMLTestRunner(stream=f,title="UnitTest Report-SJX",description='Test Case Report')

runner.run(disconver)