1、概念介绍

unit test:单元测试,可以简单粗暴地理解成用一段代码去测试另外一段代码。unittest作为Python单元测试框架之一,除了用来做单元测试之外,还可以用来完成接口自动化,UI自动化(配合Selenium使用),自动化框架开发等。

test fixture:测试用例执行前的准备工作以及测试用例执行完成后的清理工作。比如数据库测试前要建立连接,测试后要关闭连接。

test case:单元测试中最小的单元。

test suite:测试套件是测试用例,测试套件或者两者的集合。通常被用来把测试用例组织起然后交给test runner执行。

test runner:测试执行器是执行用例并向用户展示结果的组件。

unittest 官方文档链接

2、准备工作

2.1、开发环境

  • 操作系统:Ubuntu 18.04.1 LTS
  • Python版本:3.7.0
  • 开发工具:PyCharm Edu
  • 本机已安装MySQL
  • 代码结构

MySQ安装的链接

通过Python访问MySQL数据库的链接

2.2、创建数据库和表

登录数据库创建数据库ums,在数据库中创建表user_info,SQL语句如下:

create database ums;

#status分为active和inactive两种
create table user_info(
id int(10) primary key auto_increment,
name char(15) not null,
password char(100) not null,
status char(10) not null)
ENGINE=InnoDB DEFAULT CHARSET=utf8;

MySQ常用命令的链接

2.3、编写简单的注册登录代码

在Project下新建Python包 userManage ,在该包下创建Python文件userRegLogin.py。注册时先判断是否存在状态为active的用户,不存在则更新信息到数据库中。登录时先判断用户状态,如果为active则去数据库中查询其密码并比较密码是否正确。userRegLogin.py的代码如下:

#coding:utf-8
#导入MySQL驱动
import mysql.connector
import warnings #定义类user_ange
class user_manage():
#初始化 传入两个参数:用户名和密码
def __init__(self,name,passwd):
self.name = name
self.passwd = passwd #执行select SQL语句并返回结果
def execQuerySql(self,sql,arg):
#临时屏蔽告警ResourceWarning
warnings.simplefilter("ignore", ResourceWarning)
try:
self.conn = mysql.connector.connect(host="127.0.0.1",user='root',database='ums',password='password')
self.cursor = self.conn.cursor()
self.cursor.execute(sql,[arg])
val = self.cursor.fetchone()[0]
return val
except Exception as e:
print(e)
finally:
self.cursor.close()
self.conn.close() #执行insert语句
def execUpdateSql(self,sql,args):
warnings.simplefilter("ignore", ResourceWarning)
try:
self.conn = mysql.connector.connect(host="127.0.0.1",user='root',database='ums',password='password')
self.cursor = self.conn.cursor()
self.cursor.execute(sql,args)
self.conn.commit()
except Exception as e:
print(e)
finally:
self.cursor.close()
self.conn.close #判断用户是否存在
def userIsExist(self):
sql1 = '''select count(*) from user_info where status='active' and name = %s'''
userCount = self.execQuerySql(sql1,self.name)
if userCount:
return False
else:
return True #用户注册
def userReg(self):
lenFlag = len(self.passwd) >=6 and len(self.passwd)<=10
#判断是否存在同名用户
if self.userIsExist():
#判断密码长度是否符合要求
if lenFlag:
sql2 = '''insert into user_info values (null,%s,%s,'active');'''
# self.cursor.execute(sql2,[self.name,self.passwd])
# self.conn.commit()
args = [self.name,self.passwd]
self.execUpdateSql(sql2,args)
return "regSucess"
else:
return "passwordLenError"
else:
return "SameNameError" def isActive(self):
sql3 = '''select status from user_info where name=%s;'''
# self.cursor.execute(sql3,[self.name])
# ustatus = self.cursor.fetchone()[0]
ustatus = self.execQuerySql(sql3,self.name)
if ustatus == "active":
return True
else:
return False #用户登录
def userLogin(self):
'''
用户状态为active则校验密码是否正确
反之则抛出异常
'''
if self.isActive():
sql4 = '''select password from user_info where name=%s and status="active";'''
pwdInDB = self.execQuerySql(sql4,self.name)
if self.passwd == pwdInDB: return "loginSucess"
else:
return "passwordError"
else:
return "UserStatusError"

2.4、运行结果

在userRegLogin.py文件末尾插入代码并执行

if __name__ == '__main__':
#实例化
user1 = user_manage("TestUser1","1234User1")
user1.userReg()

登录数据库查看结果

mysql> select  *  from  user_info;
+----+-----------+-----------+--------+
| id | name | password | status |
+----+-----------+-----------+--------+
| 3 | TestUser1 | 1234User1 | active |
+----+-----------+-----------+--------+
1 row in set (0.00 sec)

点击这里返回目录

2.5、测试场景

  1. 注册时存在同名状态为active的用户,返回SameNameError
  2. 注册成功,返回regSucess
  3. 注册密码长度不符合要求,返回passwordLenError
  4. 登录成功,返回loginSucess
  5. 登录密码不对,返回passwordError
  6. 登录状态为inactive,返回UserStatusError

3、一个简单的例子

在Project下新建Python包testCases,在testCases下新建Python文件userRegTest.py,用来编写测试用户注册功能的代码。

[示例1]:userRegTest.py

#coding:utf-8
#导入unittest模块
import unittest
#从模块userRegLogin中导入类user_manage
from userManage.userRegLogin import user_manage
#定义测试类,继承于unittest.TestCase
class regTest(unittest.TestCase):
#测试方法或者叫测试用例必须以test开头
#测试场景:密码长度小于6
def test_pwdlen(self):
user2 = user_manage("TestUser4","1234")
self.assertEqual(user2.userReg(),"passwordLenError")
#测试场景:正常注册
def test_normalreg(self):
user2 = user_manage("TestUser5","1234User")
self.assertEqual(user2.userReg(),"regSucess") #执行test开头的方法
if __name__ == '__main__':
unittest.main()

[示例1运行结果]:

  1. 测试类继承与unittest.TestCase
  2. 一个测试用例就是Python中的一个函数。
  3. 测试用例必须要以test开头

4、test fixture

test fixture包含两个方法,setUp(self)和tearDown(self)分别用于执行测试用例前的准备工作和执行完测试用例后的清理工作。这个两个方法会在每个用例的执行前或执行后被调用。把准备和清理工作和测试用例放在会导致很多重复代码且不易维护。test fixture的使用场景到底是什么样的呢?

  1. 场景一:比如说用户注册时会先校验数据库中是否存在状态为active的同名用户,那么该用例执行过之后,需要清理user_info表中的记录,这个动作就是在tearDown(self)方法中完成的,否则再次执行就会报错。

  2. 场景二:要验证同名用户名无法注册的异常场景,需要先注册一次或者手动在user_info表里面插入数据,这个动作就是在setUp(self)中完成的。

4.1、setUp和tearDown示例

把被测类实例化放在setUp()f方法里面。正常注册场景的清理动作:删除表user_info中对应的记录放在tearDown()方法中。接下来完善一下示例1中的代码:

[示例2]:userRegTest.py

#coding:utf-8
#导入unittest模块
import unittest
#从模块userRegLogin中导入类user_manage
from userManage.userRegLogin import user_manage #定义测试类,继承于unittest.TestCase
class regTest(unittest.TestCase):
#测试方法或者叫测试用例必须以test开头
#测试场景:密码长度小于6
def setUp(self):
print("setUp run before test case")
self.user1 = user_manage("TestUser1","1234")
self.user2 = user_manage("TestUser2","TestUser2")
self.user3 = user_manage("TestUser3","TestUser3") #注册TestUser3
self.user3.userReg() def test_pwdlenerr_L1(self):
print("test case:test_pwdlenerr_L1")
res = self.user1.userReg()
self.assertEqual(res,"passwordLenError") #测试场景:正常注册
def test_regsucess_L0(self):
print("test case:test_regsucess_L0")
res = self.user2.userReg()
self.assertEqual(res,"regSucess") #测试场景:用户名重名
def test_regagain_L1(self):
print("test case:test_regagain_L1")
res = self.user3.userReg()
self.assertEqual(res,"SameNameError") def tearDown(self):
print("tearDown run after test case")
sql = '''delete from user_info where name = %s'''
self.user2.execUpdateSql(sql,[self.user2.name])
self.user3.execUpdateSql(sql,[self.user3.name]) if __name__ == '__main__':
unittest.main()

[示例2运行结果]:

  1. 如果setUp()执行成功,那么不论用例是否执行成功,tearDown()都会执行。
  2. 如果setUp()执行失败,那么用例及tearDown()方法都不会被执行。

如果只想在所有用例执行之前只执行一次准备工作怎么操作呢?那就需要用到setUpClass() 和 tearDownClass()了。在这两个方法内部可以自己编写函数实现准备工作或清理动作。

4.2、setUpClass 和 tearDownClass

[示例3]:

#定义测试类,继承于unittest.TestCase
class regTest(unittest.TestCase):
#测试方法或者叫测试用例必须以test开头
#测试场景:密码长度小于6
@classmethod
def setUpClass(cls):
print("setUpClass run before test case") def test_pwdlen(self):
print("test case:test_pwdlen")
self.user1 = user_manage("TestUser8","1234")
res = self.user1.userReg()
self.assertEqual(res,"passwordLenError") #测试场景:正常注册
def test_normalreg(self):
print("test case:test_normalreg")
self.user2 = user_manage("TestUser10","123456")
res = self.user2.userReg()
self.assertEqual(res,"regSucess") @classmethod
def tearDownClass(cls):
# sql = '''delete from user_info where name = %s'''
# sef.user2.execUpdateSql(sql,[self.user2.name])
print("tearDownClass run after test case")
#执行test开头的方法
if __name__ == '__main__':
unittest.main()

注意:没有清理动作如果想要用例跑成功的话需要手动删除表里对应的用户信息或者修改注册时传入的name。

[示例3运行结果]::

点击这里返回目录

5、测试套

5.1、登录功能测试

在包testcases下新建Python文件userLoginTest.py,编写测试登录功能的代码。

[ 示例4 ]\:userLoginTest.py

#coding:utf-8
import unittest
from userManage.userRegLogin import user_manage class loginTest(unittest.TestCase):
#准备工作
def setUp(self):
self.user4 = user_manage("TestUser4","TestUser4")
self.user5 = user_manage("TestUser4","TestUser5")
self.user6 = user_manage("TestUser6","TestUser6") #验证登录功能前需要先注册
self.user4.userReg()
#构造一个状态为inactive的用户
self.user6.userReg()
setStatus = '''update user_info set status="inactive" where name=%s;'''
self.user6.execUpdateSql(setStatus,[self.user6.name])
#登录成功测试
def test_loginsucess_L0(self):
res = self.user4.userLogin()
self.assertEqual(res,"loginSucess")
#密码错误测试
def test_pwdwrong_L0(self):
res = self.user5.userLogin()
self.assertEqual(res,"passwordError")
#用户状态异常测试
def test_statuserr_L1(self):
res = self.user6.userLogin()
self.assertEqual(res,"UserStatusError")
#清理工作
def tearDown(self):
print("tearDown run after test case")
#删除用户TestUser4,TestUser6
sql = '''delete from user_info where name = %s'''
self.user4.execUpdateSql(sql,[self.user4.name])
self.user6.execUpdateSql(sql,[self.user6.name]) if __name__ == '__main__':
unittest.main()

[ 示例4运行结果 ]:

5.2、组织用例

unittest通过类unittest.TestSuite来组织测试用例。这个类返回测试用例或测试套的集合,它可以被test runner执行。运行一个测试套相当于test runner把测试套迭代,然后执行每一个测试用例。 一些方法可以将用例添加到测试套中。

  1. addTest(test):添加一个TestCase或TestSuite到套件中。
  2. addTests(tests):把TestCase和TestSuite中给的所有的测试实例添加到套件中。

TestSuite和TestCase都有如下方法:

  1. countTestCases():返回测试用例的数量。
  2. run(result):运行套件相关的测试用例,收集测试结果到result对象中并传给result。

在Project下创建Python文件run.py,通过TestSuite来组织注册登录所有的用例并运行。

[ 示例5 ]:run.py

#coding:utf-8
import unittest #从testCase包里面导入测试类
from testCases.userLoginTest import loginTest
from testCases.userRegTest import regTest #构造测试套
def suite():
suite = unittest.TestSuite()
suite.addTest(loginTest("test_loginsucess_L0"))
suite.addTest(loginTest("test_pwdwrong_L0"))
suite.addTest(loginTest("test_statuserr_L1"))
suite.addTest(regTest("test_pwdlenerr_L1"))
suite.addTest(regTest("test_regsucess_L0"))
suite.addTest(regTest("test_regagain_L1"))
return suite #运行测试用例
if __name__ == '__main__':
runner = unittest.TextTestRunner()
#调用test runner的run方法执行用例
runner.run(suite())

[ 示例5运行结果 ]:

点击这里返回目录

Python3+unitest自动化测试初探(上篇)的更多相关文章

  1. Python3+unitest自动化测试初探(中篇)

    目录 6.生成测试报告 7.编写邮件发送工具 8.发送邮件 发布 0 86 编辑 删除 Python3+unitest自动化测试初探(中篇)(2019-04-18 01:41) 发布 3 245 编辑 ...

  2. Python3+unitest自动化测试初探(下篇)

    目录 9.用例结果校验 10.跳过用例 11.Test Discovery 12.加载用例 unittest官方文档 本篇随笔承接: Python3+unitest自动化测试初探(中篇) Python ...

  3. Python3 Selenium自动化测试赋值出现:WebDriverException: Message: unknown error: call function result missing 'value'

    Python3 Selenium自动化测试赋值出现:WebDriverException: Message: unknown error: call function result missing ' ...

  4. 第9期《python3接口自动化测试》课程,6月29号开学!

    2019年 第9期<python3接口自动化测试>课程,6月29号开学! 主讲老师:上海-悠悠 上课方式:QQ群视频在线教学 本期上课时间:6月29号-7月28号,每周六.周日晚上20:3 ...

  5. 18年selenium3+python3+unittest自动化测试教程(下)

    第六章 自动化测试进阶实战篇幅 1.自动化测试实战进阶之网页单选性别资料实战 简介:讲解使用selenium修改input输入框和单选框 2.自动化测试之页面常见弹窗处理 简介:讲解使用seleniu ...

  6. 18年selenium3+python3+unittest自动化测试教程(上)

    第一章 自动化测试课程介绍和课程大纲 1.自动化测试课程介绍 简介:讲解什么是自动化测试和课程大纲讲解,课程需要的基础和学后的水平 python3.7+selenium3 pycharm 第二章自动化 ...

  7. 浏览器自动化测试初探 - 使用phantomjs与casperjs

    收录待用,修改转载已取得腾讯云授权 作者:yangchunwen 首先要解释一下为什么叫浏览器自动化测试,因为本文只关注发布后页面功能的自动化测试,也就是UI层面的自动化. 浏览器测试有别于js代码的 ...

  8. Python3+Selenium3自动化测试-(二)

    python3 元素定位和操作方法总结 # coding=utf-8 ''' #8种元素定位方法 find_element_by_id() find_element_by_name() find_el ...

  9. [转] 浏览器自动化测试初探:使用 phantomjs 与 casperjs

    [From] https://www.qcloud.com/community/article/641602001489391648 作者:yangchunwen 首先要解释一下为什么叫浏览器自动化测 ...

随机推荐

  1. Oracle中的decode()函数

    一.decode()函数 1.语法 DECODE(control_value,value1,result1[,value2,result2-][,default_result]); control _ ...

  2. balancer.go

    package) , {         close(b.upc)     }     return nil } func getHost(ep string) string {     url, u ...

  3. Java 线程池(ThreadPoolExecutor)原理分析与使用

    在我们的开发中"池"的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 使用线程池的好处 1.降低资源消耗 可以重复利用 ...

  4. 【prufer编码】BZOJ1430 小猴打架

    Description 一开始森林里面有N只互不相识的小猴子,它们经常打架,但打架的双方都必须不是好朋友.每次打完架后,打架的双方以及它们的好朋友就会互相认识,成为好朋友.经过N-1次打架之后,整个森 ...

  5. BZOJ_3436_小K的农场_差分约束

    BZOJ_3436_小K的农场_差分约束 题意: 小K在MC里面建立很多很多的农场,总共n个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得 一些含糊的信息(共m个),以下列三种形式描述 ...

  6. BZOJ_2662_[BeiJing wc2012]冻结_分层图最短路

    BZOJ_2662_[BeiJing wc2012]冻结_分层图最短路 Description “我要成为魔法少女!”     “那么,以灵魂为代价,你希望得到什么?” “我要将有关魔法和奇迹的一切, ...

  7. JQuery 将div中的内容替换掉

    $("#div2").children().replaceWith($("#userText").val());谷歌没有效果. $("#div2&qu ...

  8. Java I/O流详解与应用

    注:学习之前,需要自己思考,不要盲目的去看!!!!!不然没有任何意义 I/O流(一) 1.定义:实现设备之间的传输 2.作用:实现数据的读入和写出 3.分类:根据操作分为:输入流和输出流 根据类型分为 ...

  9. css中固定宽高div与不固定宽高div垂直居中的处理办法

    固定高宽div垂直居中 如上图,固定高宽的很简单,写法如下: position: absolute; left: 50%; top: 50%; width:200px; height:100px; m ...

  10. Android 7.0 启动篇 — init原理(一)(转 Android 9.0 分析)

    ========================================================          ================================== ...