软件测试-2-接口测试自动化工具和unittest自动化测试框架

Python+Request接口自动化

1 接口测试自动化工具

1.1 什么是接口

在这里插入图片描述

一、 定义 
接口泛指实体把自己提供给外界的一种抽象化物(可以为另一实体),用以由内部操作分离出外部沟通方法,使其能被内部修改而不影响外界其他实体与其交互的方式。

人类与电脑等信息机器或人类与程序之间的接口称为用户界面。电脑等信息机器硬件组件间的接口叫硬件接口。电脑等信息机器软件组件间的接口叫软件接口

在计算机中,接口是计算机系统中两个独立的部件进行信息交换的共享边界。这种交换可以发生在计算机软、硬件,外部设备或进行操作的人之间,也可以是它们的结合。

二、接口的优势
1、规范性
接口就是规范,在整个系统设计中,涉及到很多层,为了使各个层之间调用透明话,你只需要知道接口,按照这个接口做你具体做的事情,就可以融合到整个系统中了。

生活中的例子很多,例如:插头、插座,有标准的规范告诉你插头应该是几个脚,插座是几个孔等等,做插头、插座的公司就是根据这个规范来做插头、插座,而不需要做完一个插头就跑遍全世界去试用一下这个插头做的对不对。

2、扩展性
在项目开发过程中,由于客户的需求经常变化,如果不采用接口,那么我们必须不停改写现有的业务代码。改写代码可能产生新的BUG,而且改写代码还会影响到调用该业务的类,可能全都需要修改,影响系统本身的稳定性。到最后,可能会出现代码凌乱,不易读懂,

后接手的人无法读懂代码,系统的维护工作越来越重,最终可能导致项目失败。

3、接口在项目就是一个业务逻辑,面向接口编程就是先把客户的业务提取出来,作为接口。业务具体实现通过该接口的实现类来完成。当客户需求变化时,只需编写该业务逻辑的新的实现类,不需要改写现有代码,减少对系统的影响。从而让项目具有更大的扩展性。

三、常见的接口类型
接口是指外部系统与系统之间以及内部各子系统之间的交互点。包括外部接口、内部接口,内部接口又包括:上层服务与下层服务接口、同级接口。

常见web接口
一类是http协议的接口。
一类是web service接口(如soup、rmi、rpc协议)

四、常见的http请求方式
get(查)、post(增),除此之外还有put(改)、delete(删)等。日常工作中见到的最多的是get和post两种。

1、GET
GET可以说是最常见的了,它本质就是发送一个请求来取得服务器上的某一资源。

(1)格式:请求数参数写在网址后面,用"?“连接,多个参数之间用”&"连接。

(2)场景:get型接口用于获取信息,多用于查询数据,如列表查询功能,点击查询按钮就调用一个get接口,然后把信息返回出来

(3)特点:1)请求数据量小,2)参数暴露于url地址中,故存在安全隐患。

2、POST
向服务器提交数据。这个方法用途广泛,几乎目前所有的提交操作都是靠这个完成。它用来向指定资源提交数据进行处理请求(例如:提交表单和上传文件),数据包被包含在请求体中,post请求可能导致新的资源的建立或者已有的资源的修改。

(1)说明:向指定资源位置提交数据(如提交表单、上传文件)来进行请求,post请求可能会导致新资源的建立。

(2)场景:如注册、上传、发帖等功能,如用户在豆瓣网站对某本书进行收藏、写笔记、发表评论。

(3)特点:请求数据量大,安全性高。

五、get和post的区别
这个问题,面试中经常被提到。简单来说,可以从三个方面去回到这个区别:方式、大小、安全。

1、方式
方式指的是参数的传入方式。
GET方法一般是指获取服务器上的数据,参数直接跟着URL后边,直接可以放到浏览器地址栏里,例如登录就是采用GET方法。
POST方法是指客户端给服务器上提交表单数据,所以POST是通过表单提交的,例如你网页上的新用户的注册、调查问卷和答题就是采用POST方法。

2、大小
上面已经知道GET是直接在浏览器地址栏输入,由于浏览器有限制,一般整个URL的长度可以很长,但是不能超过2049KB的大小限制,而这个POST就没有大小限制。

3、安全性
由于GET的参数是在浏览器地址栏直接拼接,暴露在互联网中,肯定不安全。POST是通过表单数据提交,相对比GET方法更安全。

1.2 什么是接口测试

一、接口测试
接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。

测试的重点是要检查数据的交换,传递和控制管理过程,以及系统间的相互逻辑依赖关系等。

一般来说,测试接口,就是指测试接口的功能,性能和稳定性测试,当然可能还有安全性测试。一般来说我们听说到的基本上都是指HTTP或者HTTPS协议的接口测试,也就是一些web服务请求。

二、接口
一个软件项目中,有很多接口,少的有几十个,多的有几百上千个接口。这个时候,我们没有软件界面,没有具体的测试场景,只有一个接口描述文档。我们需要把接口这样抽象的东西,通过软件测试的理论和方法去测试接口,找出接口的功能和安全性的缺陷。

接口有内部接口和外部接口。内部接口就是开发人员自己开发的接口。外部接口,好比网站调用微信支付和支付宝支付接口。还有一些模块与模块之间的接口。

三、为什么要做接口测试
1、现在很多系统前后端架构是分离的,因为不同端(前端,后端)的工作进度不一样,所以我们要针对最开始出来的接口,以及需要调用其他公司的(银行,支付宝,微信,qq等)一些接口进行接口测试及验证数据,从安全层面来说,只依赖前端进行限制已经完全不能满足系统的安全要求(绕过前端太容易了), 需要后端同样进行控制,在这种情况下就需要从接口层面进行验证。在这种情况下就需要从接口层面进行验证。前后端传输、日志打印等信息是否加密传输也是需要验证的,特别是涉及到用户的隐私信息,如身份证,银行卡等。

2、如今系统越来越复杂,传统的靠前端测试已经大大降低了效率,而且现在我们都推崇测试前移也叫测试左移,希望测试能更早的介入测试,那接口测试就是一种及早介入的方式。

例如传统测试,你是不是得等前后端都完成你才能进行测试,才能进行自动化代码编写。 而如果是接口测试,只需要前后端定义好接口,那这时自动化就可以介入编写接口自动化测试代码,手工测试只需要后端代码完成就可以介入测试后端逻辑而不用等待前端工作完成。

四、优点
接口将前端和后端进行很好的分离,帮前后端实现数据交互,这样在项目初期就可以和前端工程师讨论需要的后端数据,然后后端根据需求开发后接口,将数据返回给前端,测试人员提前进入测试接口,前后端然后在各自开发自己模块。

1.3 接口测试用例

在开始接口测试之前,我们来想一下,如何进行接口测试的准备工作。或者说,接口测试的流程是什么?有些人就很好奇,接口测试要流程干嘛?不就是拿着接口文档直接利用接口测试工具测试嘛。

其实,如果只是三五个接口,你可以这么做一个临时的接口测试。但是,如果是上百个接口,或者你们公司的这个项目,第一次做接口测试,那么,我们还是很有必要严格遵守接口测试的流程。

一、接口测试的流程
接口测试也是属于功能测试,所以跟我们以往的功能测试流程并没有太大区别,测试流程依旧是:
1.测试接口文档(需求文档)。
2.根据接口文档编写测试用例(用例编写完全可以按照以往规则来编写,例如等价类划分,边界值等设计方法) 。
3. 执行测试,查看不同的参数请求,接口的返回的数据是否达到预期。
在这里插入图片描述
接口测试和功能测试一样,流程也大致遵守V模型,请看下图。
在这里插入图片描述
一般来说,接口测试左边的每个阶段,每个公司可能都侧重点不同,例如有些公司就没有需求讨论和需求评审这个阶段。不管如何,用例设计,这个是少不了,而且是重点要花时间的阶段。只有覆盖全面的接口测试用例,才能有比较好的测试接口覆盖率,才会找出更多的接口的Bug,后期接口才能越稳定。

二、为什么要写用例
功能测试用例,大家都写过。接口测试用例,很多人没有写过。在写之前,我们来讨论下,为什么要写接口用例。
(1)理清思路,避免漏测和重复测。
(2)提高测试效率。
(3)跟进测试进度。
(4)告诉领导做过。
(5)跟进重复性工作。
(6)更好的记录问题,发现问题,复现问题。
(7)同时这也是是接口测试流程中的一个产物(测试用例)。

有用例,自己做到心中有数,不要一个测试点重复测好多次,就有思路,避免漏掉测试点。跟着用例测试,避免随机测试那种没有目的性的测试,提高测试效率。
有用例,上级问你完成的进度,你好用数据回答。
有用例,用来标记你执行的结果,证明你做过测试。避免将来发生问题,人家说你没有测试,有数据和证据说话。
有用例,测出问题你可以根据用例将问题轻而易举的浮现出来,不至于等你反馈或者复现的问题时,你忘记是如何操作才回出现问题。

接口测试也需要重复跑,跑几轮,或者用自动化天天跑。这样的重复性工作,用例可以保证每次重复做的是一样的情况。

三、接口主要设计用例点

在这里插入图片描述
主要从四个方面来设计接口用例:功能,逻辑业务,异常,安全

1、功能
(1)功能是否正常;
(2)功能是否按照接口文档实现。

举例:比如博客园添加随笔,需要登录才能添加。也就是业务要求不支持游客添加随笔功能,如果设计一个没有登录的用户,然后去测试添加随笔接口,结果接口能添加到随笔,说明功能不正常,不符合需求和接口文档描述。
2、逻辑业务
(1)是否依赖业务;

举例:该接口调用之前,需要调用登录接口,如果不登录也能请求数据,不符合业务规则。

3、异常
参数异常和数据异常
(1)参数异常:关键字参数,参数为空,多参数,少参数,错误参数。
(2)数据异常:关键字数据,数据为空,长度不一致,错误数据。

举例:不管数据异常还是参数异常,测试点差不多,一个参数有key和value,key表示参数,value表示数据。

第一,看看参数和数据能不能支持关键字,例如Java中的保留关键字等等。
第二,就是参数和数据都为空,看看是否做了判断。
第三,参数多和少,例如有两个参数的接口,你需要设计一个三个参数的用例,一个只有一个参数的用例。数据那边长度不一致,例如设计很长的字符串是否支持,因为数据库创建表过程都设置好了每个字段的长度。输入错误的参数和数据,例如故意输出单词等等。

4、安全
1)cookie:有cookie才能获取数据,如果不带cookie还有信息返回,说明有问题。
2)header:正常接口带header信息,删除header看是否能够返回数据。
3)唯一识别码:app手机识别码,一般是唯一的。

安全测试主要从上面三点检查。第三个是唯一识别码,主要是指app上手机的识别码,一般很少用到,除非很严格的接口测试,例如银行app登录,需要指纹,而指纹来源手机,一般有一个手机识别码判断过程。

1.4 接口测试工具

“工欲善其事必先利其器”,目前市场上有很多支持接口测试的工具。利用工具进行接口测试,能够提高测试效率。

假如让你一天完成100个接口测试任务,你觉得你加班能完成,那么1000个、10000个…。如果有工具,可以大大提高你的效率,可以达到事半功倍,但是不是所有工具都能够支持你完成这个任务。

一、接口测试工具
接口测试工具如图:
在这里插入图片描述
1、Fiddler
首先,这是一个HTTP协议调试代理工具,说白了就是一个抓http包的工具。web测试和手机测试都能用到这个工具。既然是http协议,这个工具也能支持接口测试。

2、PostMan
Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。

这是一款google工程师开发的一个插件,可以安装到chrome浏览器上。支持不同接口测试请求,能够管理测试套件和自动化运行,弱点在于,自动化断言功能不强大。不能和代码管理库进行持续集成测试。但是,绝对是一个很好的半手工,半自动化测试工具,我一般在写自动化接口测试用例,会打开postman进行辅助测试和debug。

3、SoupUI
SoapUI是一个开源测试工具,通过soap/http来检查、调用、实现Web Service的功能/负载/符合性测试。该工具既可作为一个单独的测试软件使用,也可利用插件集成到Eclipse,maven2.X,Netbeans 和intellij中使用。

SoapUI是一个自由和开放源码的跨平台功能测试解决方案。通过一个易于使用的图形界面和企业级功能,SoapUI让您轻松, 快速创建和执行自动化功能、回归、合规和负载测试。在一个测试环境,SoapUI提供完整的测试覆盖,并支持所有的标准协议和技术。

SoapUI基于Java开发,支持多个平台,安装非常简单。

4、Java代码做接口测试
代码是万能,笔记工具也是代码开发出来的。为什么要用代码做接口自动化测试呢。因为,有些工具功能是有限制,很多公司,需要一些特定的功能,工具不支持,只好用代码进行开发。一般用Java做自动化测试,主要是利用httpclient.jar这个包,然后利用junit或者testng这样的单元测试工具,进行测试用例的开发,然后在jenkins上创建一个job,进行持续集成测试。

5、Python代码做接口测试
和Java一样,Python中利用一个很好,功能强大的第三方库requests,能够方便都创建接口自动化用例。python下单元测试框架,一般采用unittest。生成测试报告,一般选择HTMLTestRunner.py。同样,可以和jenkins做持续集成测试。

6、LoadRunner
不要以为LR只能做性能测试,loadrunner同样可以做接口自动化和接口压力测试。只是我们很多人,不会利用LR的函数,进行开发接口测试用例。

7、JMeter
JMeter同loadrunner一样,都是以性能测试出名,一般用JMeter也是做接口性能测试。例如java+Jmeter+ant+jenkins做接口性能监听测试。

以上介绍了这么多工具,基本覆盖了接口功能测试,接口自动化测试,接口性能测试。

1.5 requests应用案例

1.5.1 发送get请求接口

一、get请求无参数param

# -*- coding: utf-8 -*-
import requests
r = requests.get("https://www.baidu.com/")
print(r.status_code)  # 打印状态码
print(r.text)  # 打印文本

二、get请求有参数param

1、再发一个带参数的get请求,
如在百度搜索:西游记,
url地址为:https://www.douban.com/search?q=西游记

2、请求参数:q=西游记,
可以以字典的形式传参:{"q": "西游记"}

3、多个参数格式:
{"key1": "value1", "key2": "value2"}

注意1:豆瓣网址后不要忘记加/search;
注意2:params不要写错。

# -*- coding: utf-8 -*-
import requests
params = {"q":"西游记"}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36 Edg/95.0.1020.40'
}
# 加入请求头才会返回内容
r = requests.get("https://www.douban.com/search",params=params,headers=headers)
print(r.status_code)  # 打印状态码
print(r.text)  # 打印文本

1.5.2 发送post请求接口

https://www.httpbin.org/

在这里插入图片描述
post请求中,可以使用data传递参数,也可以使用json传递参数。

# -*- coding: utf-8 -*-
import requests
import json
url = "https://www.httpbin.org/post"
data_dict = {"key1":"value1","key2":"value2"}
data_json = json.dumps(data_dict)

'''
一、如果使用参数json传递数据,可以直接传入字典;
会自动将字典类型的对象转换为json格式;
并将Content-Type置为application/json。
'''
# response = requests.post(url=url, json=data_dict)

#*********************************************
'''
二、如果使用参数data传递数据,例如【json.dumps()】,
一定要在header中指定Content-Type
'''
req_headers = {'Content-Type': 'application/json'}
response = requests.post(url=url, headers= req_headers,data= data_json)
print(response.text)

一、使用data传递参数

# -*- coding: utf-8 -*-
import requests
import json
url = "https://www.httpbin.org/post"
data_dict = {"key1":"value1","key2":"value2"}
data_json = json.dumps(data_dict)

'''
如果使用参数data传递数据,例如【json.dumps()】,
一定要在header中指定Content-Type
'''
req_headers = {'Content-Type': 'application/json'}
response = requests.post(url=url, headers= req_headers,data= data_json)
print(response.text)

在这里插入图片描述
二、使用json传递参数

# -*- coding: utf-8 -*-
import requests
import json
url = "https://www.httpbin.org/post"
data_dict = {"key1":"value1","key2":"value2"}
data_json = json.dumps(data_dict)

'''
一、如果使用参数json传递数据,可以直接传入字典;
会自动将字典类型的对象转换为json格式;
并将Content-Type置为application/json。
'''
response = requests.post(url=url, json=data_dict)

print(response.text)

在这里插入图片描述

1.5.3 flask测试

一、服务端代码

from flask import Flask
import json
from flask import request

app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Welcome to use me!'


@app.route('/use', methods=['POST','GET'])
def use():
    if request.method == 'POST':
        try:
            raw_json = request.get_data()
            data_dict = json.loads(raw_json)
            print("获取参数",data_dict)
            result = {"name":"lucy","age":20}
            return json.dumps(result)
        except Exception as e:
            return {"error": e}
    else:
        result = {"hello":"me"}
        return json.dumps(result)

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=9000, threaded=True)

二、接口访问代码

# -*- coding: utf-8 -*-
import requests
import json
url = "http://127.0.0.1:9000/use"
data_dict = {"key1":"value1","key2":"value2"}
data_json = json.dumps(data_dict)

'''
一、如果使用参数json传递数据,可以直接传入字典;
会自动将字典类型的对象转换为json格式;
并将Content-Type置为application/json。
'''
response = requests.post(url=url, json=data_dict)

print(response.text)

2 unittest自动化测试框架

python unittest自动化测试框架总结
Python实战之unittest使用详解
unittest是python内置的单元测试框架,具备编写用例、组织用例、执行用例、输出报告等自动化框架的条件。
在这里插入图片描述

2.1 unittest工作原理

unittest是python内置的单元测试框架,具备编写用例、组织用例、执行用例、输出报告等自动化框架的条件。

unittest最核心的四部分是:TestCase,TestSuite,(TestLoader),TestRunner,TestFixture。
(1)TestCase
用户自定义的测试case的基类,调用run()方法,会依次调用setUp方法、执行用例的方法、tearDown方法

一个完整的测试单元,执行该测试单元可以完成对某一个问题的验证,完整体现在:测试前环境准备(setUp),执行测试代码(run),以及测试后环境还原(tearDown)。

(2)TestSuite
测试用例集合,可以通过addTest()方法手动增加Test Case,也可以通过TestLoader自动添加Test Case,TestLoader在添加用例时,会没有顺序。

多个测试用例的集合,测试套件或测试计划。

(3)TestLoader
加载TestCase到TestSuite中的,其中loadTestsFrom__()方法用于寻找TestCase,并创建它们的实例,然后添加到TestSuite中,返回TestSuite实例。

(4)TestRunner
运行测试用例的驱动类,可以执行TestCase,也可以执行TestSuite,执行后TestCase和TestSuite会自动管理TESTResult。

执行测试用例,并将测试结果保存到TextTestResult实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息。

(5)TestFixture
简单来说就是做一些测试过程中需要准备的东西,比如创建临时的数据库,文件和目录等,其中setUp()和setDown()是最常用的方法。

整个的流程就是首先要写好TestCase,
由TestLoader加载TestCase到TestSuite,
然后由TestTestRunner来运行TestSuite,
运行的结果保存在TextTestReusult中,

整个过程集成在unittest.main模块中。

2.2 unittest应用方式

使用unittest对mathfunc进行单元测试,首先需要导入unitest框架和待测模块mathfunc,定义的测试用例方法类
需要继承unittest.TestCase,且测试用例方法是以test开头作为标识,用例的执行结果以assetxxx断言结果
决定,如果断言返回为false,将抛出assetError异常。

2.2.1 待测模块mathfunc.py

创建一个函数集mathfunc.py。

def add(a,b):
    return a+b
 
def minus(a, b):
    return a-b
 
def multi(a, b):
    return a*b
 
def divide(a, b):
    return a/b

为这些方法写一个测试。

2.2.2 测试用例方法类test_mathfunc.py

# -*- coding: utf-8 -*-
import unittest
from mathfunc import add,minus,multi,divide


class TestMathFunc(unittest.TestCase):

    def setUp(self):
        print("do something before test.prepare environment")

    def tearDown(self):
        print("do something after test.Clean up")

    def test_add(self):
        print("测试add")
        self.assertEqual(3, add(1, 2))
        self.assertNotEqual(3, add(2, 2))

    @unittest.skip("I don't want to run this case")
    def test_minus(self):
        print("测试minus")
        self.assertEqual(1, minus(3, 2))

    def test_multi(self):
        print("测试multi")
        self.assertEqual(6, multi(2, 3))

    def test_divide(self):
        print("测试divide")
        self.assertEqual(2, divide(6, 3))
        self.assertEqual(2.5, divide(5, 2))


if __name__ == '__main__':
    unittest.main()
    #可以直接生成report报告
    #unittest.main(testRunner=HtmlTestRunner.HTMLTestRunner(output='example_dir'))

在这里插入图片描述
上面的代码演示了如何编写一个简单的测试,下面说一下怎么控制用例执行的顺序。

2.2.3 控制用例执行顺序test_suite.py

# -*- coding: utf-8 -*-
import unittest
from test_mathfunc import TestMathFunc

if __name__ == '__main__':
    suite = unittest.TestSuite()

    tests = [TestMathFunc("test_add"),
             TestMathFunc("test_minus"),
             TestMathFunc("test_divide")]
    suite.addTests(tests)

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

在这里插入图片描述
执行情况跟我们预料的一样,执行了三个case,并且顺序是按照我们添加进去suite的顺序执行的。

2.2.4 结果输出到文件

修改test_suite.py

# -*- coding: utf-8 -*-
import unittest
import HtmlTestRunner
from test_mathfunc import TestMathFunc
if __name__ == '__main__':
    suite = unittest.TestSuite()

    # 使用这种方法可以对测试用例排序
    # tests = [TestMathFunc("test_add"),
    #          TestMathFunc("test_minus"),
    #          TestMathFunc("test_divide")]
    # suite.addTests(tests)

    # 使用TestLoader的方法传入TestCase
    suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestMathFunc))

    # # 在同目录下生成txt格式的测试报告
    # filetxt = open('UnittestTextReport.txt', 'a')
    # runner = unittest.TextTestRunner(stream=filetxt, verbosity=2)
    # runner.run(suite)
    # filetxt.close()

    # 生成HTML格式的测试报告
    filehtml = open('HTMLReport.html', 'w',encoding="utf8")
    runner = HtmlTestRunner.HTMLTestRunner(stream=filehtml,
            report_title='测试报告',
            report_name='测试用例的执行情况',
            verbosity=1
    )
    runner.run(suite)
    filehtml.close()

在这里插入图片描述

2.3 总结

1、unittest是python自带的单元测试框架,可以用来作为我们自动化测试框架的用例组织执行框架。

2、unittest流程:写好TestCase,然后由TestLoader加载TestCase到TestSuite,然后由TextTestRunner来运行TestSuite,运行的结果保存在TextTestResult中,我们通过命令行或者unittest.main()执行时,main会调用TextTestRunner中的run来执行,或者我们可以直接通过TextTestRunner来执行用例。

3、一个class继承unittest.TestCase即是一个TestCase,其中以test开头的方法在load时被加载为一个真正的TestCase。

4、verbosity参数可以控制执行结果的输出,0是简单报告、1是一般报告、2是详细报告。

5、可以通过addTest和addTests向suite中添加case或suite,可以用TestLoader的loadTestsFrom__()方法。

6、用 setUp()、tearDown()、setUpClass()以及 tearDownClass()可以在用例执行前布置环境,以及在用例执行后清理环境

7、我们可以通过skip,skipIf,skipUnless装饰器跳过某个case,或者用TestCase.skipTest方法。

8、参数中加stream,可以将报告输出到文件:可以用TextTestRunner输出txt报告,以及可以用HTMLTestRunner输出html报告。

3 实战举例

3.1 待测模块myfunc

myfunc包含三个函数:isprime(),add(),divide(),如下:

# 函数名最好不要出现下划线
def isprime(num):
    # 质数
    if num < 0 or num in (0,1):
        return False
    for element in range(2,num):
        if num % element == 0:
            return False
    return True

def add(a,b):
    return a+b

def divide(a,b):
    return a/b

3.2 编写用例

使用unittest对myfunc进行单元测试,首先需要导入unitest框架和待测模块myfunc,定义的测试用例方法类需要继承unittest.TestCase,且测试用例方法是以test开头作为标识,用例的执行结果以assetxxx断言结果决定,如果断言返回为false,将抛出assetError异常。

测试用例代码test_myfunc_1.py如下:

# -*- coding: utf-8 -*-
import unittest
from myfunc import isprime,add,divide

# 定义测试方法类,需要继承TestCase类
class TestMyFunc(unittest.TestCase):
    def setUp(self):
        print("每个用例执行前都会调用setUp方法准备环境")
    def tearDown(self):
        print("每个用例执行后会调用tearDown方法进行环境清理")

    # 测试用例以test_开头
    def test_isprime(self):
        print("测试函数isprime")
        self.assertTrue(isprime(5))
        self.assertFalse(isprime(8))
        self.assertFalse(isprime(0))
        self.assertFalse(isprime(1))
        self.assertFalse(isprime(-3))

    def test_add(self):
        print("测试函数add")
        self.assertEqual(3,add(1,2))
        self.assertNotEqual(3,add(2,2))

    def test_divide(self):
        print("测试函数divide")
        self.assertEqual(2,divide(6,3))
        self.assertNotEqual(2,divide(5,2))

if __name__ == "__main__":
    #1 注意观察用例的执行顺序
    #2 注意观察setUp和teardown的执行
    unittest.main()
    #3 跳过用例
    #4 输出html报告

在这里插入图片描述
在空白处点击运行程序,如果在某个函数处右键运行,会只运行所在处的函数。

3.3 解决四个问题

3.3.1 如何控制用例执行顺序

在unittest中,用例是以test_开头的方法定义的,默认执行顺序是根据用例名称升序进行,如上面的用例,
实际执行顺序为:test_add–>test_divide–>test_isprime,而不是用例定义的先后顺序。

在unittest中解决用例执行顺序的问题是使用TestSuite,代码如下:

# -*- coding: utf-8 -*-
import unittest
from myfunc import isprime,add,divide

# 定义测试方法类,需要继承TestCase类
class TestMyFunc(unittest.TestCase):
    def setUp(self):
        print("每个用例执行前都会调用setUp方法准备环境")
    def tearDown(self):
        print("每个用例执行后会调用tearDown方法进行环境清理")

    # 测试用例以test_开头
    def test_isprime(self):
        print("测试函数isprime")
        self.assertTrue(isprime(5))
        self.assertFalse(isprime(8))
        self.assertFalse(isprime(0))
        self.assertFalse(isprime(1))
        self.assertFalse(isprime(-3))

    def test_add(self):
        print("测试函数add")
        self.assertEqual(3,add(1,2))
        self.assertNotEqual(3,add(2,2))

    def test_divide(self):
        print("测试函数divide")
        self.assertEqual(2,divide(6,3))
        self.assertNotEqual(2,divide(5,2))

if __name__ == "__main__":
    suite = unittest.TestSuite()

    tests = [TestMyFunc("test_isprime"),TestMyFunc("test_add"),TestMyFunc("test_divide")]
    # 将测试用例实例增加到测试套件
    ## 单个添加
    suite.addTest(TestMyFunc("test_divide"))
    suite.addTest(TestMyFunc("test_isprime"))
    suite.addTest(TestMyFunc("test_add"))
    ## 批量添加
    # suite.addTests(tests)

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

这里addTest()方法没有起到作用,还是按照原来的顺序执行了。原因是:在pycharm中,引入了unittest模块,会默认按照unittest模式执行,需要将unittest模式转换成普通模式。

3.3.2 如何让多个用例共用setup和teardown

unittest的setup、teardown会在每个用例执行前后执行一次,如上面测试用例类中有3个测试用例,那么每个用例执行前会执行setup,执行后会执行teardown,即setup、teardown总共会调用三次,但考虑实际自动化测试场景,多个用例只需执行一次setup,全部用例执行完成后,执行一次teardown,针对该种场景,unittest的处理方法是使用setupclass、teardownclass,注意@classmethod的使用。

# -*- coding: utf-8 -*-
import unittest
from myfunc import isprime,add,divide

# 定义测试方法类,需要继承TestCase类
class TestMyFunc(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        print("所有用例执行前会调用一次setUp方法准备环境")
    @classmethod
    def tearDownClass(cls):
        print("所有用例执行后会调用一次tearDown方法进行环境清理")

    # 测试用例以test_开头
    def test_isprime(self):
        print("测试函数isprime")
        self.assertTrue(isprime(5))
        self.assertFalse(isprime(8))
        self.assertFalse(isprime(0))
        self.assertFalse(isprime(1))
        self.assertFalse(isprime(-3))

    def test_add(self):
        print("测试函数add")
        self.assertEqual(3,add(1,2))
        self.assertNotEqual(3,add(2,2))

    def test_divide(self):
        print("测试函数divide")
        self.assertEqual(2,divide(6,3))
        self.assertNotEqual(2,divide(5,2))

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

在这里插入图片描述

3.3.3 如何跳过用例

在自动化测试中,经常会遇到挑选用例的情况,在unittest中的解决方法是使用skip装饰器,其中skip装饰器主要有3种:

unittest.skip(reason)、
unittest.skipIf(condition,reason)、
unittest.skipUnless(condition,reason)

即在满足condition条件下跳过该用例,reason用于描述跳过的原因,实例代码如下:

# -*- coding: utf-8 -*-
import unittest
from myfunc import isprime,add,divide

# 定义测试方法类,需要继承TestCase类
class TestMyFunc(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        print("所有用例执行前会调用一次setUp方法准备环境")
    @classmethod
    def tearDownClass(cls):
        print("所有用例执行后会调用一次tearDown方法进行环境清理")

    # 测试用例以test_开头
    def test_isprime(self):
        print("测试函数isprime")
        self.assertTrue(isprime(5))
        self.assertFalse(isprime(8))
        self.assertFalse(isprime(0))
        self.assertFalse(isprime(1))
        self.assertFalse(isprime(-3))
        
    @unittest.skip("I don't want to run this test")
    def test_add(self):
        print("测试函数add")
        self.assertEqual(3,add(1,2))
        self.assertNotEqual(3,add(2,2))

    def test_divide(self):
        print("测试函数divide")
        self.assertEqual(2,divide(6,3))
        self.assertNotEqual(2,divide(5,2))

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

在这里插入图片描述

3.3.4 如何生成html格式的测试报告

Unittest中默认生成的报告格式为txt,如果想生成html格式的报告,可以使用HtmlTestRunner模块,
安装后导入该模块,使用HTMLTestRunner代替默认的TextTestRunner()执行测试用例即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

皮皮冰燃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值