unittest单元测试框架

简介:

它主要用于单元测试,还适用于web自动化测试用例的开发与执行,它可以组织执行测试用例,并且提供丰富的断言方法,判断测试用例是否通过,最终生成测试结果。

接口测试工具缺点:

1.测试数据不可控制:需要手动向数据库插入测试数据;
2.无法测试加密接口:遇到对接口参数进行加密/解密的接口或者接口的参数需要使用时间戳,工具很难模拟;
3.扩展性差:如果想生成HTML格式的测试报告或者对接口测试做定时任务,工具难以实现。

接口自动化测试设计:

测试过程:
    1.向测试数据库插入测试数据(一般使用测试数据库,不对真实数据库造成污染);
    2.调用被测系统接口;
    3.接口对数据进行处理,并返回结果;
    4.通过单元测试框架断言接口返回的数据,并生成测试报告。

unittest模块的各个属性说明:

1.TestCase:TestCase类,所有测试用例类继承的基本类,我们写的测试类都要继承它。
2.unittest.main():将一个单元测试模块变为可直接运行的测试脚本,它主要使用TestLoader类来搜索所有包含在该模块中以‘test’命名开头的测试方法,并自动执行他们。执行顺序是:按照ASCII码的顺序加载测试用例。
3.TestSuite():用来创建测试套件的。
4.TextTestRunner():是用来执行测试用例的,使用其中的run(suite)方法,来执行suite锁组装的测试用例。
5.unittest.skip():装饰器,可以用来跳过某些不想执行的测试用例.
6.TextTestResult():测试结果会保存到该实例中,包含了运行了多少实例,成功或失败等信息。

类属性:

# TestCase类
1.setUp():该方法用于测试用例执行前的初始化工作。例如测试用例中需要访问数据库,可以在该方法中建立数据库连接并进行初始化。
    
2.tearDown():tearDown()方法用于测试用例执行之后的善后工作。如关闭数据库连接。关闭浏览器。

3.assert*():一些断言方法:在执行测试用例的过程中,最终用例是否执行通过,是通过判断测试得到的实际结果和预期结果是否相等决定的。

4.assertEqual(a,b,[msg='测试失败时打印的信息']):断言a和b是否相等,相等则测试用例通过。

5.assertNotEqual(a,b,[msg='测试失败时打印的信息']):断言a和b是否相等,不相等则测试用例通过。

6.assertTrue(x,[msg='测试失败时打印的信息']):断言x是否True,是True则测试用例通过。

7.assertFalse(x,[msg='测试失败时打印的信息']):断言x是否False,是False则测试用例通过。

8.assertIs(a,b,[msg='测试失败时打印的信息']):断言a是否是b,是则测试用例通过。

9.assertNotIs(a,b,[msg='测试失败时打印的信息']):断言a是否是b,不是则测试用例通过。

10.assertIsNone(x,[msg='测试失败时打印的信息']):断言x是否None,是None则测试用例通过。

11.assertIsNotNone(x,[msg='测试失败时打印的信息']):断言x是否None,不是None则测试用例通过。

12.assertIn(a,b,[msg='测试失败时打印的信息']):断言a是否在b中,在b中则测试用例通过。

13.assertNotIn(a,b,[msg='测试失败时打印的信息']):断言a是否在b中,不在b中则测试用例通过。

14.assertIsInstance(a,b,[msg='测试失败时打印的信息']):断言a是是b的一个实例,是则测试用例通过。

15.assertNotIsInstance(a,b,[msg='测试失败时打印的信息']):断言a是是b的一个实例,不是则测试用例通过。

# TestSuite类
1.addTest(): addTest()方法是将测试用例添加到测试套件中,如下方,是将test_baidu模块下的BaiduTest类下的test_baidu测试用例添加到测试套件。
2.addTests(): 可以添加多个测试用例(可迭代的参数)
代码:
suite = unittest.TestSuite()           suite.addTest(test_baidu.BaiduTest('test_baidu'))

# TextTestRunner类
run(): run()方法是运行测试套件的测试用例,入参为suite测试套件。
代码:
runner = unittest.TextTestRunner()
runner.run(suite)

三种实现方式:

# coding=utf-8
#1.先设置编码,utf-8可支持中英文,如上,一般放在第一行

#2.注释:包括记录创建时间,创建人,项目名称。
'''
Created on 2016-7-27
@author: Jennifer
Project:使用unittest框架编写测试用例思路
'''
#3.导入unittest模块
import unittest

#4.定义测试类,父类为unittest.TestCase。
#可继承unittest.TestCase的方法,如setUp和tearDown方法,不过此方法可以在子类重写,覆盖父类方法。
#可继承unittest.TestCase的各种断言方法。
class Test(unittest.TestCase): 
    
#5.定义setUp()方法用于测试用例执行前的初始化工作。
#注意,所有类中方法的入参为self,定义方法的变量也要“self.变量”
#注意,输入的值为字符型的需要转为int型
    def setUp(self):
        self.number=raw_input('Enter a number:')
        self.number=int(self.number)

#6.定义测试用例,以“test_”开头命名的方法
#注意,方法的入参为self
#可使用unittest.TestCase类下面的各种断言方法用于对测试结果的判断
#可定义多个测试用例
#最重要的就是该部分
    def test_case1(self):
        print self.number
        self.assertEqual(self.number,10,msg='Your input is not 10')
        
    def test_case2(self):
        print self.number
        self.assertEqual(self.number,20,msg='Your input is not 20')

    @unittest.skip('暂时跳过用例3的测试')
    def test_case3(self):
        print self.number
        self.assertEqual(self.number,30,msg='Your input is not 30')

#7.定义tearDown()方法用于测试用例执行之后的善后工作。
#注意,方法的入参为self
    def tearDown(self):
        print 'Test over'
        
#8如果直接运行该文件(__name__值为__main__),则执行以下语句,常用于测试脚本是否能够正常运行
if __name__=='__main__':
#8.1执行测试用例方案一如下:
#unittest.main()方法会搜索该模块下所有以test开头的测试用例方法,并自动执行它们。
#执行顺序是命名顺序:先执行test_case1,再执行test_case2
    unittest.main()

'''
#8.2执行测试用例方案二如下:
#8.2.1先构造测试集
#8.2.1.1实例化测试套件
    suite=unittest.TestSuite()
#8.2.1.2将测试用例加载到测试套件中。
#执行顺序是安装加载顺序:先执行test_case2,再执行test_case1
    suite.addTest(Test('test_case2'))
    suite.addTest(Test('test_case1'))
#8.2.2执行测试用例
#8.2.2.1实例化TextTestRunner类
    runner=unittest.TextTestRunner()
#8.2.2.2使用run()方法运行测试套件(即运行测试套件中的所有用例)
    runner.run(suite)
'''
    
'''
#8.3执行测试用例方案三如下:
#8.3.1构造测试集(简化了方案二中先要创建测试套件然后再依次加载测试用例)
#执行顺序同方案一:执行顺序是命名顺序:先执行test_case1,再执行test_case2
    test_dir = './'
    discover = unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py')
#8.3.2执行测试用例
#8.3.2.1实例化TextTestRunner类
    runner=unittest.TextTestRunner()
#8.3.2.2使用run()方法运行测试套件(即运行测试套件中的所有用例)
    runner.run(discover)   
'''

案例:

import unittest
import requests
import os, sys
parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, parentdir)
from db_fixture import test_data
 
 
class AddEventTest(unittest.TestCase):
  ''' 添加发布会 '''
 
  def setUp(self):
    self.base_url = "http://127.0.0.1:8000/api/add_event/"
 
  def tearDown(self):
    print(self.result)
 
  def test_add_event_all_null(self):
    ''' 所有参数为空 '''
    payload = {'eid':'','':'','limit':'','address':"",'start_time':''}
    r = requests.post(self.base_url, data=payload)
    self.result = r.json()
    self.assertEqual(self.result['status'], 10021)
    self.assertEqual(self.result['message'], 'parameter error')
 
  def test_add_event_eid_exist(self):
    ''' id已经存在 '''
    payload = {'eid':1,'name':'一加4发布会','limit':2000,'address':"深圳宝体",'start_time':'2017'}
    r = requests.post(self.base_url, data=payload)
    self.result = r.json()
    self.assertEqual(self.result['status'], 10022)
    self.assertEqual(self.result['message'], 'event id already exists')
 
  def test_add_event_name_exist(self):
    ''' 名称已经存在 '''
    payload = {'eid':11,'name':'红米Pro发布会','limit':2000,'address':"深圳宝体",'start_time':'2017'}
    r = requests.post(self.base_url,data=payload)
    self.result = r.json()
    self.assertEqual(self.result['status'], 10023)
    self.assertEqual(self.result['message'], 'event name already exists')
 
  def test_add_event_data_type_error(self):
    ''' 日期格式错误 '''
    payload = {'eid':11,'name':'一加4手机发布会','limit':2000,'address':"深圳宝体",'start_time':'2017'}
    r = requests.post(self.base_url,data=payload)
    self.result = r.json()
    self.assertEqual(self.result['status'], 10024)
    self.assertIn('start_time format error.', self.result['message'])
 
  def test_add_event_success(self):
    ''' 添加成功 '''
    payload = {'eid':11,'name':'一加4手机发布会','limit':2000,'address':"深圳宝体",'start_time':'2017-05-10 12:00:00'}
    r = requests.post(self.base_url,data=payload)
    self.result = r.json()
    self.assertEqual(self.result['status'], 200)
    self.assertEqual(self.result['message'], 'add event success')
 
 
if __name__ == '__main__':
  test_data.init_data() # 初始化接口测试数据
  unittest.main()

生成HTML测试报告:

当开发的接口达到一定数量后,就需要考虑 分文件分目录 的来 划分 接口测试用例,如何批量的执行不同文件目录下的用例呢?unittest单元测试框架提供的 discover() 方法可以帮助我们做到这一点。并使用 HTMLTestRunner 扩展生成 HTML 格式的测试报告。

使用unittest框架所提供的discover()方法,查找 interface/ 目录下,所有匹配_test.py 的测试文件(星 号匹配任意字符)。

import time, sys
sys.path.append('./interface')
sys.path.append('./db_fixture')
from HTMLTestRunner import HTMLTestRunner
import unittest
from db_fixture import test_data
 
 
# 指定测试用例为当前文件夹下的 interface 目录
test_dir = './interface'
discover = unittest.defaultTestLoader.discover(test_dir, pattern='*_test.py')
 
 
if __name__ == "__main__":
  test_data.init_data() # 初始化接口测试数据
 
  now = time.strftime("%Y-%m-%d %H_%M_%S")
  filename = './report/' + now + '_result.html'
  fp = open(filename, 'wb')
  runner = HTMLTestRunner(stream=fp,
              title='Guest Manage System Interface Test Report',
              description='Implementation Example with: ')
  runner.run(discover)
  fp.close()

HTMLTestRunner 为unittest单元测试框架的扩展,利用它所提供的HTMLTestRunner()类来替换unittest单元测试框架的TextTestRunner()类,从而生成HTML格式的测试报告。

遗憾的是HTMLTestRunner并不支持Python3.x,大家可以在网上找到适用于Python3.x的HTMLTestRunner.py文件,使用在自己的接口自动化工程中。

通过 time 的 strftime()方法获取当前时间,并且转化成一定的时间格式。作为测试报告的名称。这样做目的是是为了避免因为生成的报告的名称重名而造成报告的覆盖。最终,将测试报告存放于report/目录下面。如下图,一张完整的接口自动化测试报告。

 

posted @ 2019-05-21 19:54  起个名字、真难啊  阅读(886)  评论(1编辑  收藏  举报