一、环境

系统测试环境版本
windows10python3.6及以上
Pychram

2021.3

Selenium        4.1.0
浏览器驱动

Chrome: --

Edge: --

二、测试框架-unittest

Selenium常用且兼容性更好的是pytest,但是个人觉得unittest框架也需要去熟悉一下,这里只是记录下学习笔记

a. 特点:

        1. 测试发现: 从多个py文件中收集并且加载测试用例

        2. 测试执行:将测试用例按照一定的顺序和条件去执行并生成结果

        3. 测试判断:通过断言去判断结果是否正确

        4. 测试报告:统计测试进度和通过率并生成报告
 

b. 默认规则

        1. 测试文件必须先导入Import unittest

        2. 测试类必须基础unittest.TestCase

        3. 测试方法必须以test开头
 

c. 使用方法

1. 新建一个类的继承
    class TestCase(unittest.TestCase):
          xxx

2. 导入unittest
    import unittest

3. 准备一个以test开头的方法:
    def test_xx(self):
         xxx


d. 运行方式

1. 命令行方式(推荐):

python -m unittest path\xxx.py or

示例: python -m unittest test_login.py

python -m unittest path\xx.TestCase.xx

示例: python -m unittest login.TestCase.test_01_login

2. main方式

启动方式,尽量和测试用例区分路径,按下图配置

注: 本机使用了Miniconda3来构建虚拟环境,有兴趣的可以自行查看,用虚拟环境会有很多便利性,这里暂时先不提了


e. 重要组件

TestCase

TestCase类的实例表示unittest中的逻辑测试单元,此类旨在用作基类,具体测试由具体的子类实现。该类实现了测试运行器所需的接口,以允许它驱动测试,

以及测试代码可用于检查和报告各种故障的方法。每个实例都将运行unittest的一个名为methodName的基本方法

import unittest

class Test(unittest.TestCase)
# 测试方法
    def test_01(self):
        xxxx

运行方式:

1. 在项目目录下使用unittest命令行方式运行

        python -m unittest 模块名.类名.方法名 -v -k

        # 方法名为可选项,如果不带方法名,则默认运行当前类下的所有方法

        -v 为输出详细结果,可选

        -k 为匹配模式,可选

        1). *为通配符,以字符串为结尾运行类似用例,-k *_test
        2). 字符串,以具体字符串去匹配-k test

2. 使用unittest.main(),以模块的方式运行

        1). 配置pychram环境,Edit Configurations-->python-->add-->scrpit path-->目标模块
        2). python xxx.py直接运行

运行结果:

. 代表成功
F 代表失败
E代表错误
S代表跳过

运行顺序:

按照ASCII码执行[0-9,A-Z,a-z], 实际上是按照字典的排序进行的

框架原理:

module =='__main__'                                                                           # 测试用例所在路径 __main__表示当前模块,可以自定义任何模块
defaultTest=None                                                                                 # 需要测试的用例名称,默认为所有 例如: defaultTest=[“类.方法”]
argv=None #接收外部的参数,很少使用
testRunner=None #测试运行器,Text TestRunner
testLoader=loader.defult.TestLoader                                                    ​​​指定使用默认的测试用例加载器
exit=True 是否在测试完成之后结束python程序
verbosity=1                                                                         ​​​​​​​        ​​​​​​​        ​​​​​​​  ​# 类似于命令行模式-v, [0是什么都没有,1是常规模式 “.”,2是详细模式 “xxxx .... ok”]
failfast=None
catchbreak=None
buffer=None
warning=None
tb_locals=False
注: 标红是经常使用的


TestSuite

TestCase类的实例表示unittest中的逻辑测试单元,此类旨在用作基类,具体测试由具体的子类实现。该类实现了测试运行器所需的接口,以允许它驱动测试,

以及测试代码可用于检查和报告各种故障的方法。每个实例都将运行unittest的一个名为methodName的基本方法

addTest()

if __name__='__main__'
    suite= unittest.TestSuite()
    suite.addTest(类(“方法”))
    unittest.main(defaultTest='suite')

addTest 是添加单个方法,如果要添加多个方法可以多行添加,也可以用addTests添加测试集

addTests()

if __name__='__main__'
    suite= unittest.TestSuite()
    testcases=[类(“方法1”), 类(“方法2”)]
    suit.addTests(testcases)
    unittest.main(defaultTest='suite')

addTests 将迭代TestCase和TestSuite实例中的所有测试用例添加到此测试组件, 相当于多次调用addTest


TestFixture-测试夹具

1. setUp/tearDown

setUp(): 该方法的主要作用是用来初始化测试环境,它在测试用例执行之前立即调用,除了AssertionError或SkipTest,通过该方法产生的任何异常都将被认为是错误的。

只有测试成功执行,才会被调用,默认什么都不做

tearDown():  该方法的主要作用是在测试用例执行完毕后记录测试结果并恢复测试环境,即使出现异常,也会调用此方法。

即每个测试用例的开始与结束被执行

测试示例:

def setUp(self):    
    print(“setUp: 在每个测试用例执行前执行,例如打开网页,加载网页”)

def tearDown (self):
    print(“tearDown: 在每个测试用例结束后执行,例如关闭网页”)

2. setUpClass/tearDownClass

在每个测试类的开始与结束时被执行

@classmethod 			#装饰器,这是类方法
def setUPClass(cls):	
    print(“setUpClass: 在每个类之前执行一次.例如创建数据库,生成日志”)

def teardown(cls):
    print(“teardown:在 每个类之后执行一次.例如关闭数据库链接,销毁日志对象”)

setUpClass/tearDownClass为类方法,需要通过@classmethod 进行装饰,另外方法的参数为cls,cls和self没有本质上的区别,都只表示方法的第一个参数

3. setUpModule/tearDownModule

在整个模块的开始与结束时被执行

def setUpModule():
    print(“模块级别夹具开始”)

class Test(unittest.TestCase):
    ....

def tearDownModule():
    print(“模块级别夹具结束”)

总结:

1. 六个测试夹具都是非必要条件,如果没有可以使用pass 占位符即可

2. 使用方法可以新建一个模块,将前置后置放在当中,然后测试用例直接继承该类,注意setUpModule和tearDownModule放在具体执行的模块中

from xx.py import calssname
class TestCase(classname):
    xxxx

TestLoader

TestLoader类被用来创建类和模块的测试套件,通常不需要创建该类的实例,unittest框架提供了一个可以共享的实例unittest.defaultTestLoader,示例如下:

if __name__='__main__'
    suite= unittest.TestSuite()
    testcases=unittest.defaultTestLoader.discover(start_dir=os.getcwd(),pattern='test*.py')         
    #start_dir从哪个目录开始, os.getcwd()为获取当前项目路径, pattern为测试内容支持正则匹配
    unittest.main(defaultTest='suite')

TestRunner

TextTestRunner 和 HTMLTestRunner  都是在 TestRunner 进行的拓展,它们都是调用父类(TestRunner)的方法来执行测试用例,这里我们简单说下TextTestRunner

TextTestRunner 有很多参数,暂时了解三种:TextTestRunner(stream=None, descriptions=None, verbosity=0),和 TestCase()当中的框架原理类似

if __name__='__main__'
    suite= unittest.TestSuite()
    suite.addTest(类(“方法”))
    unittest.TextTestRunner().run(suite)


f. 忽略测试用例

1. @unittest.skip("理由")

2. @unittest.skipif(条件, "理由")

        当条件为True,跳过执行

3. @unittest.skipunless(条件, "理由")

        当条件为False, 跳过执行

可以作用到具体的用例(方法)或者整个模块(), 一般作用于具体的用例


g. 断言

assertEqual(a,b)                 断言a==b
assertNotEqual(a,b)           断言a!=b
assertTrue(a)                      断言a为真
assertFalse(a)                     断言a为假
assertIn(a,b)                       断言a属于b
assertNotin(a,b)                  断言a不属于b


​​​​​​​h. HTML报告

使用第三方的库HTMLTestRunner.py文件,存放到python*\lib目录下即可

下载地址:
http://tungwaiyip.info/software/HTMLTestRunner.html

由于该文件是python2的时候写的,很多环境不支持python3,需要参照以下修改

1、第94行         import StringIO
     更改为:       import io
2、第539行       self.outputBuffer = StringIO.StringIO()
     更改为:       self.outputBuffer = io.StringIO()
3、第631行       print >>sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime)
     更改为:       sys.stderr.write('\nTime Elapsed: %s\n' % (self.stopTime - self.startTime))
4、第642行       if not rmap.has_key(cls):
     更改为:       if cls not in rmap:
5、第687行       self.stream.write(output.encode('utf8'))
     更改为:       self.stream.write(output)
6、第766行       uo = o.decode('latin-1')
     更改为:       uo = o
7、第772行       ue = e.decode('latin-1')
     更改为:      ue = e
8、第778行       output = saxutils.escape(uo+ue),
     更改为:      output = saxutils.escape(str(uo)+ue),

示例如下:

import os, unittest
from HTMLTestRunner import HTMLTestRunner

if __main__ == ‘__main__’:
    suite = unittest.TestSuite()
	testcases = unittest.defaultTestLoader.discover(start_dir=os.getcwd(), pattern='*.py')
	suite.addTests(testcases)
	report_name = open(os.getcwd()+"/report.html","wb")
	runner=HTMLTestRunner(stream=report_name,verbosity=2,title="dynaConnect", description="报告详情如下:")
	runner.run(suite)

# stream为报告名称(附路径)
# verbosity类似于命令行模式-v 2代表详细模式
# title 报告标题
# description 报告描述

notice

测试用例(方法)下加三个双引号里面的内容可以作为测试用例的注解出现在报告当中

import unittest

class Test(unittest.TestCase)
    # 测试方法
    def test_1(self):
        """
        测试一
        """
        xxxx
    
    def test2(self):
        xxxx
    
    def test3(self):
        xxxx

"""测试一"""


i. unittest+ddt数据驱动及ddt底层

当数据文件中有多组数据,代码只有一份,通过代码将数据分离,解耦合

驱动模式介绍:

数据驱动模式
关键字驱动模式(核心: 把业务逻辑封装成关键字)
混合驱动模式(关键字驱动+数据驱动)====>主流
行为驱动模式: Lettuce ====>目前级基本没有使用

ddt解释:

data driver test 数据驱动测试 特点:可以完美和应用于Unittest框架实现数据驱动
ddt是通过装饰器的形式来调用的,装饰器是完成一种特定功能的函数(事务)
在python里面装饰器是以@开头的,装饰器有两种: 类装饰器、函数装饰器

ddt装饰器:

@ddt (类装饰器,申明当前类使用ddt框架)  #ddt是第三方的包,需要额外安装

@data (函数装饰器,用于给测试用例传递数据, 可以传数字,字符串,列表,元组,集合,字典)

示例:
数字:@data(10)
字符串: @data('test')
列表: @data(['test1','test2'])
元组: @data(('test1','test2'))
集合: @data({'test1','test2'})
字典: @data({"name":"test1", "value":"test2"})

@ddt和@data是配合使用的,使用需要导入包 from ddt import ddt, data, 当传单个值时,所有类型都可以传入,并且用例执行一次,当传几个值时,用例就会执行几次,例如
@data(['test1', 'test2'],['test3', 'test4']), 执行结果为:
['test1', 'test2']
['test3', 'test4']

@unpack (函数装饰器,将传输的数据包解包),解包一般用于元组tuple和列表list

示例:
@data(('test1', 'test2'), ('test3', 'test4'))
@unpack
#注:当解包元素的时候,元组里面有几个值,那么传递给方法的时候就要配几个参数
def test_01(self, args1, args2):   
        print(args1, args2)
        print(type(args1))
#未解包之前数据类型为元组,解包之后数据类型为字符串

*由于集合是无顺序的,所以不支持解包

*字典解包需要符合严格的格式

@data({"name":"test1", "age":"18"}, {"name":"test2", "age":"19"})
@unpack
#注: 字典解包传入的参数,必须是字典的键值,并且要完全一致,缺一不可        
def test_02(self, name, age):
        print(name, age)

@file_data (函数装饰器,可以直接读取 yaml/json文件)​​​​​​​


j. unittest具体实例

通过读取Excel内容,将当中的用户名密码赋值给内部搭建的网站进行用户名密码登录测试,并生成HTML报告

import os
import unittest
import time
import click as click
import openpyxl
from HTMLTestRunner import HTMLTestRunner
from ddt import ddt, data, unpack
from selenium import webdriver
from selenium.webdriver.common.by import By

def read_excel():
    workbook=openpyxl.load_workbook(os.path.abspath(os.path.dirname(__file__)+"/login_data.xlsx")
    sheet = workbook["login"]
    alllist = []
    for row in range(2, sheet.max_row + 1):  # 遍历行数
        templist = []
        for col in range(1, sheet.max_column + 1):
            templist.append(sheet.cell(row, col).value)  # 遍历列数
        alllist.append(templist)
    return alllist  # 如果不return,函数调用完之后返回值为空

@ddt
class Test(unittest.TestCase):
    @data(*read_excel())  # *表示以列表的方式解析这个值
    @unpack
    def test_login(self, order, username, password):
        """测试登陆"""
        global driver
        driver = webdriver.Edge()
        driver.get("http://192.168.100.81:82/users/sign_in")
        driver.find_element(By.ID, "user_login").send_keys(username)
        driver.find_element(By.ID, "user_password").send_keys(password)
        time.sleep(2)
        driver.find_element(By.XPATH,"/html/body/div[1]/div[2]/div/div[3]/div[2]/div/div/div/div/form/div[5]/input").click()

        if order == 1:
            self.assertEqual(1,2)
        elif order == 2:
            self.assertEqual(1,1)
        else:
            self.assertEqual(1,2)


if __name__ == '__main__':
    suite = unittest.TestSuite()				    
    testcases=unittest.defaultTestLoader.discover(start_dir=os.path.abspath(os.path.dirname(__file__)), pattern='test.py')
    suite.addTests(testcases)
    nowtime = time.strftime("%Y-%m-%d %H-%M-%S", time.localtime())
    report_name = open(os.path.abspath(os.path.dirname(__file__) + "/" + nowtime + "report.html", "wb")
    runner=HTMLTestRunner(stream=report_name,verbosity=2,title="dynaConnect",description="报告详情如下: ")
    runner.run(suite)

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐