python单元测试unittest

Python中有一个自带的单元测试框架是unittest模块,用它来做单元测试,它里面封装好了一些校验返回的结果方法和一些用例执行前的初始化操作。
在说unittest之前,先说几个概念:
TestCase 也就是测试用例
TestSuite 多个测试用例集合在一起,就是TestSuite
TestLoader是用来加载TestCase到TestSuite中的
TestRunner是来执行测试用例的,测试的结果会保存到TestResult实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息
下面写一个简单的单元测试用例

        import unittest
        class MyTest(unittest.TestCase):#继承unittest.TestCase
            def tearDown(self):
                #每个测试用例执行之后做操作
                print('111')
            def setUp(self):
                #每个测试用例执行之前做操作
                print(22222)
            def test_run(self):
                self.assertEqual(1,1)
                #测试用例
        
        
        if __name__ == '__main__':
            unittest.main()#运行所有的测试用例

下面是一些常用的断言,也就是校验结果

        assertEqual(a, b)     a == b      
        assertNotEqual(a, b)     a != b      
        assertTrue(x)     bool(x) is True      
        assertFalse(x)     bool(x) is False      
        assertIsNone(x)     x is None     
        assertIsNotNone(x)     x is not None   
        assertIn(a, b)     a in b    
        assertNotIn(a, b)     a not in b

那如何生成一个测试报告呢,需要加入另外一个模块了,HTMLTestRunner,这个模块需要自己安装,使用执行测试用例就会生成一个html的测试报告,里面会有每个测试用例的执行结果,代码如下:


        import HTMLTestRunner        
        import unittest
        class MyTest(unittest.TestCase):#继承unittest.TestCase
            def tearDown(self):
                #每个测试用例执行之后做操作
                print('111')
            def setUp(self):
                #每个测试用例执行之前做操作
                print(22222)
            def test_run(self):
                # self.assertEqual(1,1)
                self.assertIs(1,1)
                #测试用例
            def test_run2(self):
                # self.assertEqual(1,1)
                self.assertIs(1,1)
                #测试用例
            def test_run3(self):
                # self.assertEqual(1,1)
                self.assertIs(1,1)
                #测试用例
            def test_run1(self):
                # self.assertEqual(1,1)
                self.assertIs(1,1)
                #测试用例
        if __name__ == '__main__':
            test_suite = unittest.TestSuite()#创建一个测试集合
            test_suite.addTest(MyTest('test_run1'))#测试套件中添加测试用例
            #test_suite.addTest(unittest.makeSuite(MyTest))#使用makeSuite方法添加所有的测试方法
            fp = open('res.html','wb')#打开一个保存结果的html文件
            runner = HTMLTestRunner.HTMLTestRunner(stream=fp,title='api测试报告',description='测试情况')
            #生成执行用例的对象
            runner.run(test_suite)
            #执行测试套件

如果我们有很多个模块,每个模块下面都写了很多python文件,每个python文件里面都有测试用例,那怎么把这个目录下的用例都执行了呢,就要先找到这个目录下的所有python文件,然后找到里面的测试用例,逐个执行,代码如下:


        import unittest,HTMLTestRunner
        suite = unittest.TestSuite()#创建测试套件
        all_cases = unittest.defaultTestLoader.discover('.','test_*.py')
        #找到某个目录下所有的以test开头的Python文件里面的测试用例
        for case in all_cases:
            suite.addTests(case)#把所有的测试用例添加进来
        fp = open('res.html','wb')
        runner = HTMLTestRunner.HTMLTestRunner(stream=fp,title='all_tests',description='所有测试情况')
        runner.run(suite)
        #运行测试

我们在后续进行持续集成的时候,要让代码自动运行,就会用到Jenkins了,但是上面产生的测试报告都是html格式的,Jenkins不认识,就在Jenkins里面显示不出来。那咱们就要产生一些Jenkins认识的测试报告,Jenkins认识xml格式的报告,那咱们就产生xml格式的呗,就需要用一个新的模块,xmlrunner,安装直接 pip install xmlrunner即可,代码如下:


import unittest
import xmlrunner
#导入这个模块class My(unittest.TestCase):
 
    def test1(self,a,b,c):
        self.assertEqual(a+b,c) 
if __name__=='__main__':
    test_suite = unittest.TestSuite()
    test_suite.addTest(unittest.makeSuite(My))    runner = xmlrunner.XMLTestRunner(output='report')#指定报告放的目录
    runner.run(test_suite)

然后咱们运行,可以看到在report目录下已经产生了xml格式的报告了,而且还自动把日期加上了

上面的报告打开并不是很美观,下面介绍一种更美观的报告:

import unittest
import HTMLTestRunner
import BeautifulReport as bf    #as起别名
#import parameterized

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

# 1.正常,整数   2.负数
class CalcTest(unittest.TestCase):


    def test_h2(self):
        print('test2')
        '''负数'''
        result = calc(-1,2)
        self.assertNotEqual(1,result)   #不通过

    def test_h3(self):
        print('test3')
        '''负数'''
        result = calc(-1,3)
        self.assertEqual(1,result,msg='计算加法结果不正确,h3')  #不通过

    def test_h1(self):
        print('test1')
        '''正数'''
        result = calc(1,2)
        self.assertEqual(3,result,msg='计算加法结果不正确')

#运行当前文件所有的测试用例
# unittest.main()

#2.用TestRunner运行测试用例才可以产生报告

# suite = unittest.TestSuite()
suite = unittest.makeSuite(CalcTest)

#不好看的报告
# f = open('report.html','wb')
# runner = HTMLTestRunner.HTMLTestRunner(f,title='端午测试报告',description='这是自动化测试的报告')
# runner.run(suite)
#好看的报告
runner = bf.BeautifulReport(suite)
runner.report('端午节测试报告','bf_report.html')

这样出来的报告结果如下:

如果每次请求都一样,只是传的参数不一样,那我们还得自己写多个函数来验证,这样代码写的比较复杂,也不利于维护,那么有没有让unitest读取参数化好的文件的方法呢?请看下面的代码:

import requests
import unittest
from parameterized import parameterized as pd
import BeautifulReport as bf

def read_file(file_name):
    result = []
    with open(file_name,encoding='utf-8') as fr:
        for line in fr:
            line = line.strip()
            if line:
                result.append(line.split(','))
    return result



class InterFaceCase(unittest.TestCase):


    # @pd.expand([
    #     ['http://api.nnzhp.cn/api/user/stu_info','stu_name=矿泉水','矿泉水'],
    #     ['http://api.nnzhp.cn/api/user/stu_info', 'stu_name=小黑', '小黑'],
    #     ['http://api.nnzhp.cn/api/user/stu_info','stu_name=','小黑']
    # ])
    @pd.expand(read_file('login.txt'))
    def testlogin(self,url,data,check):
        '''login测试用例'''
        url = url+'?'+data
        result = requests.get(url).text
        self.assertIn(check,result)

#unittest.main()

suite = unittest.makeSuite(InterFaceCase)
runner = bf.BeautifulReport(suite)
runner.report('端午节测试报告','para_bf_html')

我们可以利用parameterized来传参数,既可以传一个list,也可以用文件来传参数,上面的例子就是用txt文件来传参数。
测试完的结果如下:

如果我要测试的接口依赖上一个接口返回的值,那么应该怎么做呢?也就是性能测试中经常用到的关联,请参考下面的用例:

import unittest

class BaseTest(unittest.TestCase):


    def register(self):
        username,password = 'user1','123456'
        print('注册成功')
        return username,password


    def login(self,username,password):
        print('test login',username,password)
        return 'token1'

    def add_basket(self,sku_id,count,token):
        print('加入购物车',sku_id,count)
        return

    def order(self,token):
        print('产生订单')
        return 'order_id'

    def pay(self,order_id):
        print('支付')


class TestOrder(BaseTest):
    def testOrder(self):
        '''生成订单的正常流程'''
        token = self.login('user1','test1')
        order_id = self.order(token)

    def testOrder2(self):
        '''生成订单的异常流程'''
        token = self.login('user1','test1')
        order_id = self.order(token)

class TestPay(BaseTest):
    def testPay(self):
        self.register('uuu','pass111')




    # def testbuy(self,token):
    #     # token = self.login()
    #
    #     print('test buy')
    #     print('使用%s购物成功'%token)


unittest.main()


我们可以先定义一个类,里面写好每个操作步骤需要传入和返回的参数,然后再写测试类,让测试类继承上面的类(基类),这样就可以调用开始写好的函数了,也就可以取得相应的返回值。

除此之外,还有一点知识需要介绍一下,请看下面的代码:

import unittest

class Test(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        print('setupClass')

    @classmethod
    def tearDownClass(cls):
        print('tearDownClass')

    def setUp(self):
        print('set up')

    def tearDown(self):
        print('teardown')

    def test(self):
        print('test!')

    def test2(self):
        print('test2')

unittest.main()

执行结果如下:

..
setupClass
set up
test!
teardown
----------------------------------------------------------------------
set up
test2
Ran 2 tests in 0.000s
teardown

tearDownClass
OK

Process finished with exit code 0

从结果可以看出来:
setupClass最开始运行一次
tearDownClass最后运行一次
set up每次测试之前运行一次
teardown每次测试之后运行一次
可以利用这几个方法来进行数据库相关的操作,如清理测试数据等。

转自:牛牛杂货铺

posted @ 2019-06-11 15:15  狂爷  阅读(274)  评论(0编辑  收藏  举报