栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

软件测试框架之——unittest

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

软件测试框架之——unittest

通过unittest创建测试
1必须继承与unittest.TestCase类
2可以定义setUp和tearDown方法,也可以定义setUpClass、tearDownClass的类方法
3所有的测试方法必须以test开头。测试方法会在运行时自动被调用
4可用pycharm自带的unittest框架运行,也可以以普通方式运行

Python复制代码

import unittest


class Demo(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        print("setUpClass方法只运行一次")

    @classmethod
    def tearDownClass(cls):
        print("tearDownClass方法只运行一次")

    def setUp(self):
        print("setUp在每个test方法执行之前执行")

    def tearDown(self):
        print("tearDown在每个test方法执行之后执行")

    def testFunc(self):
        print("这是一个测试方法")

    def testLogin(self):
        print("这是一个测试登录的方法")


if __name__ == '__main__':
    print("普通的测试入口")
    unittest.main()

运行方式的设置
●pycharm自带的unittest框架测试
●从main方法的入口开启的unittest测试

Python复制代码

if __name__ == '__main__':
    print("普通的测试入口")
    # main方法的入口开启测试
    unittest.main()

Run/DebugConfigurations

Name:

Allowparallelrun

unittest-demo-01

Storeasprojectfile

mAN方法作为入口开启测试的配置

Python

Configuration

Logs

时STPythondocs

E:python-studylautotestunittest-demopy

Docutilstask

scriptpath.:

ASTSphinxtask

Parameters

Pythontests

Autodetect

Environment

-demo.py

Doctests

st-demo.D

PYTHONUNBUFFERED-1

Environmentvariables

Nosetests

pytest

不走main方法,使用自带的unittest进行测试

Twisted

Unittests

lnterpreteroptions

Shellscript

的配置,默认是这种配置

SpaceTask

cipython-studyautotest

tOx

AddcontentrootstoPYTHONPATH

AddsourcerootstoPYTHONPATH

Execution

Emulateterminalinoutputconsole

RunwithPythonConsole

Redirectinputfrom:

Editconfigurationtemplates..

OK

Apply

Cancel


pycharm自带的unittest框架测试
这种运行测试,main方法就不会生效。
上面的代码输出结果为:

Plain Text复制代码

setUpClass方法只运行一次
setUp在每个test方法执行之前执行
这是一个测试方法
tearDown在每个test方法执行之后执行
setUp在每个test方法执行之前执行
这是一个测试登录的方法
tearDown在每个test方法执行之后执行
tearDownClass方法只运行一次
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

设置从main方法的入口开启unittest测试
上面的输出结果为,这个就会走if __name__ == '__main__':中的代码

Plain Text复制代码

普通的测试入口
setUpClass方法只运行一次
setUp在每个test方法执行之前执行
这是一个测试方法
tearDown在每个test方法执行之后执行
setUp在每个test方法执行之前执行
这是一个测试登录的方法
tearDown在每个test方法执行之后执行
tearDownClass方法只运行一次
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK


断言
断言一旦失败了,后面的代码就不会执行
常用的断言有:
●self.assertEqual()
●self.assertNotEqual()
●self.assertIn()
●self.assertNotIn()
●self.assertTrue()
●self.assertFalse()
断言的结果表示
●点.代表断言正确
●F代表断言失败
●E代表代码有错误

实现断言失败后再截图功能
●通过异常处理的方式实现
●通过装饰器的方式实现

通过异常处理的方式

Python复制代码

import time
import unittest
from selenium import webdriver

from ddt import ddt, data, unpack
from util import Util


@ddt
class Demo(unittest.TestCase):
    
    # 其他方法省略

    def test_demo1(self):
        try:
            self.assertEqual(1, 2)
        except AssertionError as e:
            import os, time
            day = time.strftime('%Y%m%d', time.localtime(time.time()))
            sceenshot_path = os.getcwd() + r'reportsscreenshot_%s' % day
            if not os.path.exists(sceenshot_path):
                os.makedirs(sceenshot_path)

            tm = time.strftime('%H%M%S', time.localtime(time.time()))
            print(sceenshot_path + '\{}_{}.png'.format('screen_shot', tm))
            self.driver.get_screenshot_as_file(sceenshot_path + '\{}_{}.png'.format('screen_shot', tm))
            # 将异常继续抛出给unittest
            raise e


通过装饰器的方式

Python复制代码

import time
import unittest
from selenium import webdriver

from ddt import ddt, data, unpack
from util import Util


@ddt
class Demo(unittest.TestCase):

    # 省略一些方法

    def addpic(func):
        def wrapper(self, *args, **kwargs):
            try:
                func(self, *args, **kwargs)
            except AssertionError as e:
                import os, time
                day = time.strftime('%Y%m%d', time.localtime(time.time()))
                sceenshot_path = os.getcwd() + r'reportsscreenshot_%s' % day
                if not os.path.exists(sceenshot_path):
                    os.makedirs(sceenshot_path)

                tm = time.strftime('%H%M%S', time.localtime(time.time()))
                print(sceenshot_path + '\{}_{}.png'.format('screen_shot', tm))
                self.driver.get_screenshot_as_file(sceenshot_path + '\{}_{}.png'.format('screen_shot', tm))
                # 将异常继续抛出给unittest
                raise e
        return wrapper

    @addpic
    def test_demo1(self):
        self.assertEqual(1, 2)




if __name__ == '__main__':
    print("普通的测试入口")

TestLoader
自动化用例管理 TestLoader类的常见方法:
●loadTestFromTestCase():从一个python类中加载
●loadTestFromTestModule(): 从一个python文件中加载
●loadTestFromTestNames():从方法名中加载
●discover:从一个路径中加载

loadTestsFromTestCase

Python复制代码

from stringtest import StringTest
from numbertest import NumberTest
import unittest

if __name__ == '__main__':
    st = unittest.defaultTestLoader.loadTestsFromTestCase(StringTest)
    nt = unittest.defaultTestLoader.loadTestsFromTestCase(NumberTest)
    # 将TestCase装到TestSuite中
    suite = unittest.TestSuite([st,nt])
    # 使用TestRunner运行TestSuite中的所有用例
    unittest.TextTestRunner().run(suite)

loadTestsFromModule

Python复制代码

import unittest
import stringtest
import numbertest

if __name__ == '__main__':
    names = [numbertest, stringtest]
    modules = []
    for item in names:
        module = unittest.defaultTestLoader.loadTestsFromModule(item)
        modules.append(module)
    suite = unittest.TestSuite(modules)
    unittest.TextTestRunner().run(suite)

loadTestFromTestNames

Python复制代码

from stringtest import StringTest
import unittest


def getFullTestCaseName():
    pass


if __name__ == '__main__':
    names = ['stringtest.StringTest.test1', 'autotest.numbertest.NumberTest.test2']
    smoke_test = unittest.defaultTestLoader.loadTestsFromNames(names)
    suite = unittest.TestSuite([smoke_test])
    unittest.TextTestRunner().run(suite)

获取一个类中的所有用例getTestCaseNames

Python复制代码

# 获取一个类中的所有的用例
cases = unittest.defaultTestLoader.getTestCaseNames(StringTest)
print(cases)


discover

Python复制代码

case_dir = 'autotest'
# smoke代表冒烟测试的文件夹,加载冒烟测试文件夹下的所有测试用例
dis = unittest.defaultTestLoader.discover(case_dir + '/smoke', pattern='*.py')
suite = unittest.TestSuite(dis)
unittest.TextTestRunner().run(suite)


生成测试报告
使用HTMLTestRunner生成测试报告
1配置方法(将HTMLTestRunner_cn.py放到python的安装目录的site-packages目录下)
2生成测试报告
3在报告中添加截图图片
4失败重试功能
 

Python复制代码

from HTMLTestRunner import HTMLTestRunner

# 保存图片,写在需要保存图片的用例中
self.imgs.append(self.driver.get_screenshot_as_base64())

# 组装TestSuite
st = unittest.defaultTestLoader.loadTestsFromTestCase(StringTest)
nt = unittest.defaultTestLoader.loadTestsFromTestCase(NumberTest)
suite = unittest.TestSuite([st, nt])

runner = HTMLTestRunner(title='测试报告1', description='第一次生成测试报告', 
                        stream=open('test-report.html', 'wb'), verbosity=2)

# 有的版本还可携带retry=2, save_last_try=False参数。这里我下载的HTMLTestRunner版本中没有这两个参数
# retry代表断言失败重试的最大次数,中间如果成功,就不再重试。
# save_last_try代表是否只保留最后一次重试的记录,True表示只保留一次,False表示保留每一次重试的记录

runner.run(suite)
    


参数化驱动测试用例
所谓参数化,是指利用不同的测试数据来测试相同的场景,为了提高代码的重用性,增加代码效率而采用一种代码编写的方法,叫参数化,也就是数据驱动。达到测试数据和测试业务相分离的效果。
换句话说,就是通过传参的不同,一个方法可以运行多个测试用例,减少代码的冗余,提高代码的复用率。

普通的循环
使用循环来在一个方法中跑多个测试用例,虽然能减少代码的冗余,但实际上在生成的测试报告中,算一个用例,这是不好的。

Python复制代码

import time
import unittest
from selenium import webdriver


class Demo(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        print("setUpClass方法只运行一次")

    @classmethod
    def tearDownClass(cls):
        print("tearDownClass方法只运行一次")

    def setUp(self):
        print("setUp在每个test方法执行之前执行")
        self.driver = webdriver.Chrome
        self.url = '某个url地址'
        self.testdata = [
            ('', 'admin123', 'user id 不能为空'),
            ('admin', '', 'password不能为空'),
            ('adminx', 'admin123', '没有此用户'),
            ('admin', 'xxxx', '密码错误')
        ]

    def tearDown(self):
        print("tearDown在每个test方法执行之后执行")

    def testFunc(self):
        print("这是一个测试方法")
        self.assertTrue(False)

    def testLogin(self):
        print("这是一个测试登录的方法")
        self.driver.get(self.url)
        time.sleep(1)
        for data in self.testdata:
            time.sleep(1)
            self.driver.find_element_by_id('username').clear()
            self.driver.find_element_by_id('username').send_keys(data[0])
            time.sleep(1)
            self.driver.find_element_by_id('password').clear()
            self.driver.find_element_by_id('password').send_keys(data[1])
            time.sleep(1)
            self.driver.find_element_by_xpath('//input[@value="Login"]').click()
            time.sleep(1)
            errmsg = self.driver.find_element_by_xpath('//div[contains(text(),"{}")]'.format(data[2])).text
            self.assertEqual(errmsg, data[2])
            self.driver.find_element_by_xpath('//span[contains(text(),"确定")]').click()


if __name__ == '__main__':
    print("普通的测试入口")
    unittest.main()


在unittest框架中使用参数化
1安装ddt: pip install ddt
2导入相关的模块:from ddt import ddt,data,unpack
3参数化的几种形式
a参数值为单个的参数形式
b参数值为组合参数形式
c从函数中返回参数值
d从文件中返回参数值

参数值为单个的参数形式

Python复制代码

import time
import unittest
from selenium import webdriver

from ddt import ddt, data, unpack

# 这个注解是必须的
@ddt
class Demo(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        print("setUpClass方法只运行一次")

    @classmethod
    def tearDownClass(cls):
        print("tearDownClass方法只运行一次")

    def setUp(self):
        print("setUp在每个test方法执行之前执行")
       
    def tearDown(self):
        print("tearDown在每个test方法执行之后执行")

    # 单个注解的传参形式
    @data(1, 2, 3)
    def test_data(self, value):
        print(value)

参数值为多个的形式

Python复制代码

import time
import unittest
from selenium import webdriver

from ddt import ddt, data, unpack


@ddt
class Demo(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        print("setUpClass方法只运行一次")

    @classmethod
    def tearDownClass(cls):
        print("tearDownClass方法只运行一次")

    def setUp(self):
        print("setUp在每个test方法执行之前执行")
        self.driver = webdriver.Chrome
        self.url = '某个url地址'
        
    def tearDown(self):
        print("tearDown在每个test方法执行之后执行")


    @data(
        ('', 'admin123', 'user id 不能为空'),
        ('admin', '', 'password不能为空'),
        ('adminx', 'admin123', '没有此用户'),
        ('admin', 'xxxx', '密码错误')
    )
    # 表示将元组的每一行作为一条数据
    @unpack
    def testLogin2(self, username, password, err):
        print("username={},password={},err={}".format(username, password, err))

    @data(
        ('', 'admin123', 'user id 不能为空'),
        ('admin', '', 'password不能为空'),
        ('adminx', 'admin123', '没有此用户'),
        ('admin', 'xxxx', '密码错误')
    )
    @unpack
    def testLogin(self, username, password, err):
        print("这是一个测试登录的方法")
        self.driver.get(self.url)
        time.sleep(1)
        self.driver.find_element_by_id('username').clear()
        self.driver.find_element_by_id('username').send_keys(username)
        time.sleep(1)
        self.driver.find_element_by_id('password').clear()
        self.driver.find_element_by_id('password').send_keys(password)
        time.sleep(1)
        self.driver.find_element_by_xpath('//input[@value="Login"]').click()
        time.sleep(1)
        errmsg = self.driver.find_element_by_xpath('//div[contains(text(),"{}")]'.format(err)).text
        self.assertEqual(errmsg, err)
        self.driver.find_element_by_xpath('//span[contains(text(),"确定")]').click()


if __name__ == '__main__':
    print("普通的测试入口")
    unittest.main()


从函数中传来参数值
定义外部函数:

Python复制代码

class Util:

    @classmethod
    def get_data(cls):
        testdata = [
            ('', 'admin123', 'user id 不能为空'),
            ('admin', '', 'password不能为空'),
            ('adminx', 'admin123', '没有此用户'),
            ('admin', 'xxxx', '密码错误')
        ]
        return testdata

Python复制代码

import time
import unittest
from selenium import webdriver

from ddt import ddt, data, unpack
# 导入外部函数所在的类
from util import Util



@ddt
class Demo(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        print("setUpClass方法只运行一次")

    @classmethod
    def tearDownClass(cls):
        print("tearDownClass方法只运行一次")

    def setUp(self):
        print("setUp在每个test方法执行之前执行")
        self.driver = webdriver.Chrome
        self.url = '某个url地址'
        self.testdata = [
            ('', 'admin123', 'user id 不能为空'),
            ('admin', '', 'password不能为空'),
            ('adminx', 'admin123', '没有此用户'),
            ('admin', 'xxxx', '密码错误')
        ]

    def tearDown(self):
        print("tearDown在每个test方法执行之后执行")

    #
    # @data(1, 2, 3)
    # def test_data(self, value):
    #     print(value)

    @data(*Util.get_data())
    @unpack
    def testLogin2(self, username, password, err):
        print("username={},password={},err={}".format(username, password, err))



if __name__ == '__main__':
    print("普通的测试入口")
    unittest.main()


从文件中传来参数值
data.txt文件

Python复制代码

, admin123, user id 不能为空
admin, , password不能为空
adminx, admin123, 没有此用户
admin, xxxx, 密码错误

Python复制代码

class Util:
    @classmethod
    def get_data_from_file(cls, path):
        rows = []
        with open(path, 'r', encoding='utf-8') as f:
            for line in f:
                user_data = line.strip().split(',')
                rows.append(user_data)
        return rows

Python复制代码

import time
import unittest
from selenium import webdriver

from ddt import ddt, data, unpack
from util import Util



@ddt
class Demo(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        print("setUpClass方法只运行一次")

    @classmethod
    def tearDownClass(cls):
        print("tearDownClass方法只运行一次")

    def setUp(self):
        print("setUp在每个test方法执行之前执行")
        self.driver = webdriver.Chrome
        self.url = '某个url地址'

    def tearDown(self):
        print("tearDown在每个test方法执行之后执行")


    @data(*Util.get_data_from_file('data.txt'))
    # @data(*Util.get_data())
    @unpack
    def testLogin2(self, username, password, err):
        print("username={},password={},err={}".format(username, password, err))



if __name__ == '__main__':
    print("普通的测试入口")
    unittest.main()

 如果对python自动化测试、web自动化、接口自动化、移动端自动化、大型互联网架构技术、面试经验交流等等感兴趣的老铁们,可以关注我。我会在公众号(程序员阿沐)/群里(810119819)不定期的发放免费的资料链接,这些资料都是从各个技术网站搜集、整理出来的,如果你有好的学习资料可以私聊发我,我会注明出处之后分享给大家。欢迎分享,欢迎评论,欢迎转发。需要资料的同学可以关注我获取资料链接。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/348380.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号