UnitTest单元测试框架/数据驱动实战

一、简介:

在Python中,单元测试框架主要是unittest,单元测试是最基本也是最底层的测试类型,单元测试应用于最基本的软件代码,如类,函数,方法等。

 

 

 

维度:1、表单的错误提示信息验证

2、产品的业务逻辑

3、页面的各种交互

完整的自动化测试用例步骤:

1、初始化

2、测试步骤

3、断言

4、清理

二、UnitTest测试框架

UnitTest组件:

1)测试用例:testcase

2)测试固件:初始化setUp( ),清理tearDown()

3)测试套件:TestSuite在一个测试套件里面可以包含很多的测试用例,可以把它理解为一个容器

4)测试执行:TestRunner:测试执行指的是针对测试套件或者是测试用例进行执行的过程

5)测试报告:TestReport:所有的测试测试用例执行完成后输出的汇总结果报告信息

注意:在测试类里面编写的测试方法必须是test开头的

2.1以下以获取百度title为例

2.2类测试固件:

在unittest中测试固件依据方法可以分为两种执行方式:

第一种是测试固件只执行一次,它的缺点是:执行完一个测试用例后,需要回归到页面的初始化

第二种是测试固件每次都执行,下面依据具体的案例来讲解二者。

这里以登录新浪邮箱为例:测试固件每次都执行

import unittest
from selenium import webdriver
import time as t

class SinaTest(unittest.TestCase):
    def setUp(self)->None:
        self.driver=webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get('http://mail.sina.com.cn/')
        self.driver.implicitly_wait(30)

    def tearDown(self)->None:
        self.driver.quit()

    def test_login_null(self):
        """登录:验证账户密码为空的错误提示信息"""
        self.driver.find_element_by_id('freename').send_keys('')
        self.driver.find_element_by_id('freepassword').send_keys('')
        self.driver.find_element_by_class_name('loginBtn').click()
        t.sleep(3)
        divText=self.driver.find_element_by_xpath('/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]')
        #assertEqua比较两个对象是否相等,
        self.assertEqual(divText.text,'请输入邮箱名')

    def test_login_email_format(self):
        """登录:验证账户格式不规范的错误提示信息"""
        self.driver.find_element_by_id('freename').send_keys('hdsuch')
        self.driver.find_element_by_id('freepassword').send_keys('naluhc')
        self.driver.find_element_by_class_name('loginBtn').click()
        t.sleep(3)
        divText=self.driver.find_element_by_xpath('/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]')
        #assertEqua比较两个对象是否相等,
        self.assertEqual(divText.text,'您输入的邮箱名格式不正确')

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

 2.2.1分离测试固件

针对测试固件进行分离,在page包下创建init.py文件,来具体分离我们的测试固件

#分离固件
import unittest
from selenium import webdriver

class Init(unittest.TestCase):
    def setUp(self)->None:
        self.driver=webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get('http://mail.sina.com.cn/')
        self.driver.implicitly_wait(30)

    def tearDown(self)->None:
        self.driver.quit()

 

2.3 四个断言的方法:

1.assertTrue 断言是否为真

2.assert in 比较一个对象是否包含另外一个对象

3.assertFalse 断言是否为假

4.assertEqual  比较两个对象是否相等

2.4测试用例执行顺序:

        在unittest中,测试点的执行顺序是依据ascill码来执行的,也就是说根据ASCILL码的顺序加载,数字与字母的顺序为:0-9,A-Z,a-z,所以以A开头的测试用例方法会优先执行,以a开头会后执行。也就是根据数字的大小从小到 大执行的,切记数字的大小值的是不包含test,值的是test后面的测试点的数字大小。

       在自动化测试用例中,编写的测试用例不需要刻意的进行排序,让测试框架内部框架按照它的排序规则去执行。

      切记:编写的每一个自动化测试用例都必须是独立的,和其他的测试用例之间没有任何的依赖性。

2.5编写测试用例注意事项

1、在一个测试类里面,每一个测试方法都是以test开头的,test不能是中间或者尾部,必须是开头,建议test_

2、每一个测试用例方法都应该有注释信息,这样在测试报告就会显示具体的测试点的检查点

3、在自动化测试中,每个测试用例都必须得有断言,无断言的自动化测试用例是无效的

4、最好一个测试用例方法对应一个业务测试点,不要多个业务检查点写一个测试用例

5、如果涉及到业务逻辑的处理,最好把业务逻辑的处理方法放在断言前面,这样做的目的是不要因为业务逻辑执 行错误导致断言也是失败

6、测试用例名称最好规范,有约束

7、是否先写自动化测试的测试代码,在使用自动化测试方式写,本人觉得没必要,毕竟能够做自动化测试的都具 备了功能测试的基本水平,所以没必要把一个业务的检查点写多次,浪费时间和人力成本。

2.6自动化测试流程:

1)先梳理哪些模块可以做自动化测试

2)梳理完成后,和相关的人建议对下计划以及梳理的结果

3)编写代码实现梳理的测试模块

4)编写完成后相关的人进行评审代码(测试场景是否考虑周全,测试断言是否合理,代码编写的是否合理)

5)编写完成后,整合到JeKins的持续集成平台

6)下个迭代的时候,直接可以应用于产品的回归测试中

2.7测试套件详解

2.7.1按测试类执行

     可以理解为在测试套件中,我们按测试类的方式来执行,测试类里面有多少的测试用例,我们都会执行

2.7.2按测试模块执行

按测试模块来执行,就是以模块为单位来进行执行,那么其实在一个模块里面可以编写很多的类

2.7.3 按具体的测试用例来执行

 当然如果是仅仅执行某一个测试用例,执行的方式一种是鼠标放到具体的测试用例后面,然后右键执行就可以了

import unittest
from selenium import webdriver
import time as t

class BaiduTest(unittest.TestCase):
   def setUp(self)->None:
      self.driver=webdriver.Chrome()
      self.driver.maximize_window()
      self.driver.get('http://mail.sina.com.cn/')
      self.driver.implicitly_wait(30)

   def tearDown(self)->None:
      self.driver.quit()

   def test_login_email_format(self):
      """登录:验证账户格式不规范的错误提示信息"""
      self.driver.find_element_by_id('freename').send_keys('hdsuch')
      self.driver.find_element_by_id('freepassword').send_keys('naluhc')
      self.driver.find_element_by_class_name('loginBtn').click()
      t.sleep(3)
      divText=self.driver.find_element_by_xpath('/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]')
      #assertEqua比较两个对象是否相等,
      self.assertEqual(divText.text,'您输入的邮箱名格式不正确')
      #assertIn比较一个对象是否包含另外一个对象,in
      self.assertIn(divText.text,'您输入的邮箱名格式不正确')

   def test_login_isButton(self):
      """登录:验证自动登录是否默认勾选"""
      divText=self.driver.find_element_by_id('store1')
      #针对bool的类型验证assertTrue
      self.assertTrue(divText.is_selected())

if __name__=='__main__':
   # 按测试模块来执行
   #suite = unittest.TestLoader().loadTestsFromModule(module='test_sina.py')括号里写模块名
 #按测试类来执行
   suite=unittest.TestLoader().loadTestsFromTestCase(testCaseClass='SinaTest')括号里写类名
   unittest.TextTestRunner().run(suite)

2.8加载所有的测试模块(重点)

import unittest
import os
if __name__=='__main__':
   #加载所有的测试模块来执行
   #start_dir:测试模块的路径
   #pattern:通过正则的方式加载所有的测试模块
   suite=unittest.TestLoader().discover(
      start_dir=os.path.dirname(__file__),
      pattern='test_*.py')
   unittest.TextTestRunner().run(suite)

 

2.9unittest之参数化

首先需要安装一个第三方的库parameterized,安装的命令为:

pip3 install parameterized

本质思想:把测试的数据看成列表当中的一个元素, 那么针对列表进行循环的时候,把每个元素进行赋值

在UI自动化测试中,parameterized也是特别的有用,如针对一个登录案例的测试,针对登录就会有很多的测试案例,主要是用户名和密码的input表单的验证以及错误信息的验证,下面就结合具体的案例来看

以新浪邮箱登录为例:

class SinaTest(unittest.TestCase):
   def setUp(self)->None:
      self.driver=webdriver.Chrome()
      self.driver.maximize_window()
      self.driver.get('http://mail.sina.com.cn/')
      self.driver.implicitly_wait(30)
   def tearDown(self)->None:
      self.driver.quit()

   @parameterized.expand([
      param('','','请输入邮箱名'),
      param('waertre','asewrae','您输入的邮箱名格式不正确'),
      param('1754788101@qq.com','123456','请输入正确的新浪邮箱帐号和密码')
   ])
   def test_login(self,username,password,result):
      """登录:测试登录不同场景"""
      self.driver.find_element_by_id('freename').send_keys(username)
      self.driver.find_element_by_id('freepassword').send_keys(password)
      self.driver.find_element_by_class_name('loginBtn').click()
      t.sleep(3)
      divText=self.driver.find_element_by_xpath('/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]')
      #assertEqua比较两个对象是否相等,
      self.assertEqual(divText.text,result)

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

 

2.10 生成HTML测试报告

unittest生成测试报告需要使用到第三方的库HTMLTestRunner,把该库放在Python安装目录下的lib目录下。

 

 

下面具体展示测试报告的生成,把测试报告存储到report的文件夹里面,思考到每次生成的测试报告名称一致,我们可以以当前时间作为区分

import unittest
import HTMLTestRunner
import os
import time

def getSuite():
   #获取所有需要执行的测试模块
   suite=unittest.TestLoader().discover(
      start_dir=os.path.dirname(__file__),
      pattern='test_*.py')
   return suite

def getNowTime():
   return time.strftime('%y-%m-%d %H_%M_%S',time.localtime(time.time()))

def main():
   filename=os.path.join(os.path.dirname(__file__),'report',getNowTime()+'reprot.html')
   fp=open(filename,'wb')
   runner=HTMLTestRunner.HTMLTestRunner(
      stream=fp,
      title='UI自动化测试报告',
      description='UI自动化测试报告'
   )
   runner.run(getSuite())

if __name__=='__main__':
   main()

在report的文件夹下,生成的测试报告打开后显示如下:

 

 

 三、UI自动化测试实战之数据驱动

在UI的自动化测试中,我们需要把测试使用到的数据分离到文件中,如果单纯的写在我们的测试模块里面,不是一个好的设计,所以不管是什么类型的自动化测试,都是需要把数据分离出来的。当然分离到具体的文件里面,文件的形式其实有很多的,这里主要说明JSON的文件和YAML的文件在UI自动化测试中的应用。

3.1JSON文件

以下以JSON为例,注意,JSON里是{ },且都是双引号

 

 完善后的测试脚本为

import unittest
import time as t
from selenium import webdriver
import json
from unittest测试框架.init import Init
def readJson():
   return json.load(open('sina.json',encoding='utf-8'))

class SinaTest(Init):
   def test_login_null(self):
      """登录:验证账户密码为空的错误提示信息"""
      self.driver.find_element_by_id('freename').send_keys('')
      self.driver.find_element_by_id('freepassword').send_keys('')
      self.driver.find_element_by_class_name('loginBtn').click()
      t.sleep(3)
      divText=self.driver.find_element_by_xpath('/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]')
      #assertEqua比较两个对象是否相等,
      self.assertEqual(divText.text,readJson()['null'])

   def test_login_email_format(self):
      """登录:验证账户格式不规范的错误提示信息"""
      self.driver.find_element_by_id('freename').send_keys('hdsuch')
      self.driver.find_element_by_id('freepassword').send_keys('naluhc')
      self.driver.find_element_by_class_name('loginBtn').click()
      t.sleep(3)
      divText=self.driver.find_element_by_xpath('/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]')
      #assertEqua比较两个对象是否相等,
      self.assertEqual(divText.text,readJson()['format'])

if __name__=='__main__':
 #按测试类来执行
   suite=unittest.TestLoader().loadTestsFromTestCase(testCaseClass='SinaTest')
   unittest.TextTestRunner().run(suite)

 

3.2 YAML文件

     下面我们演示把测试数据存储到YAML文件里面,注意:冒号后面要有空格,不需要加引号

js java go语言中用的是 null true false;在python中用None True False。

分离出来的文件内容为:

 

 

import unittest
import time as t
from selenium import webdriver
import json
import yaml
from unittest测试框架.init import Init
def readYaml():
   with open('sina.yaml',encoding='utf-8')as f:
      return yaml.safe_load(f)

class SinaTest(Init):
   def test_login_null(self):
      """登录:验证账户密码为空的错误提示信息"""
      self.driver.find_element_by_id('freename').send_keys('')
      self.driver.find_element_by_id('freepassword').send_keys('')
      self.driver.find_element_by_class_name('loginBtn').click()
      t.sleep(3)
      divText=self.driver.find_element_by_xpath('/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]')
      #assertEqua比较两个对象是否相等,
      self.assertEqual(divText.text,readYaml()['login'][None])

   def test_login_email_format(self):
      """登录:验证账户格式不规范的错误提示信息"""
      self.driver.find_element_by_id('freename').send_keys('hdsuch')
      self.driver.find_element_by_id('freepassword').send_keys('naluhc')
      self.driver.find_element_by_class_name('loginBtn').click()
      t.sleep(3)
      divText=self.driver.find_element_by_xpath('/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]')
      #assertEqua比较两个对象是否相等,
      self.assertEqual(divText.text,readYaml()['login']['format'])

if __name__=='__main__':
 #按测试类来执行
   suite=unittest.TestLoader().loadTestsFromTestCase(testCaseClass='SinaTest')
   unittest.TextTestRunner().run(suite)

实战:login_list.json

下面将测试用例使用参数化的方法

import unittest
import time as t
from selenium import webdriver
from parameterized import parameterized,param
import json
from unittest测试框架.init import Init
"""参数化:
相同的测试步骤,不同的测试数据,那么这样的测试场景我们就可以使用
参数化的解决思路来解决。也就是说使用一个测试用例的代码,执行多个测试场景
参数化本质:针对测试数据进行循环,每次循环的时候对列表中元素的值一一赋值的过程"""
def readJson():
   return json.load(open('login_list.json',encoding='utf-8'))['login']

class SinaTest(Init):
   @parameterized.expand([
   param(readJson()[0]['username'],readJson()[0]['password'],readJson()[0]['result']),
   param(readJson()[1]['username'],readJson()[1]['password'],readJson()[1]['result']),
   param(readJson()[2]['username'],readJson()[2]['password'],readJson()[2]['result'])
   ])
   def test_login(self, username, password, result):
      # 登录:测试登录不同场景
      self.driver.find_element_by_id('freename').send_keys(username)
      self.driver.find_element_by_id('freepassword').send_keys(password)
      self.driver.find_element_by_class_name('loginBtn').click()
      t.sleep(3)
      divText = self.driver.find_element_by_xpath(
         '/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]')
      # assertEqua比较两个对象是否相等,
      self.assertEqual(divText.text, result)

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

 

posted @ 2022-04-11 17:22  lm970418  阅读(225)  评论(0编辑  收藏  举报