unittest单元测试框架
unittest单元测试框架
概述:
官网帮助文档:https://docs.python.org/zh-cn/3/library/unittest.html
单元测试框架主要用来完成以下三件事:
- 提供用例组织与执行:当测试用例只有几条时,可以不必考虑用例的组织,但是当用例达到成百上千条时,大量的用例堆砌在一起,就产生了扩展性与维护性等,单元测试框架就是用来解决用例的规范与组织问题。
- 提供了丰富的比较方法:不论是功能测试,还是单元测试,在用例执行完成之后都需要将实际结果与预期结果进行比较(断言),从而判定用例是否执行通过。单元测试框架一般会提供丰富的断言方法。例如:判断相等/不等、包含/不包含、True/False的断言方法等。
- 提供丰富的日志:当测试用例执行失败时能抛出清晰的失败原因,当所有用例执行完成后提供丰富的执行结果。例如:,总执行时间、失败用例数、成功用例数等。
一、unittest介绍
什么是单元测试?
单元测试负责对最小的软件设计单元(模块)进行验证,它使用软件设计文档中对模块的描述作为指南,对重要的程序分支进行测试以发现模块中的错误。
例如:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# 计算器类
class Count:
def __init__(self,a,b):
self.a = int(a)
self.b = int(b)
# 计算加法
def add(self):
return self.a + self.b
Test.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import unittest
from calculator import Count class TestCount(unittest.TestCase): def setUp(self):
print("test start") def test_add(self):
j = Count(2,3)
self.assertEqual(j.add(),5) def tearDown(self):
print("test end ") if __name__ == '__main__':
unittest.main()
Test Case
一个TestCase的实例就是一个测试用例;
什么是测试用例?
就是一个完整的测试流程,包含测试前环境的搭建(setUp) --->实现测试过程的代码(run)---->以及测试后环境的还原(tearDown),一个测试用例就是一个完整的测试单元,通过运行这个测试单元,可以对某一个功能进行验证。
Test Suite
一个功能的验证往往需要多个测试用例,可以把多个测试用例集合在一起执行,这就产生了测试套件TestSuite的概念。Test Suite用来组装单个测试用例。可以通过addTest加载TestCase到TestSuite中,从而返回一个TestSuite实例。
Test Runner
测试的执行也是单元测试中非常重要的一个概念,一般单元测试框架中都会提供丰富的执行策略和执行结果。在unittest单元测试框架中,通过TextTestRunner类提供的run()方法来执行test suite/test case 。test runner可以使用图形界面、文本界面、或返回一个特殊的值等方式来表示测试执行的结果。
Test Fixture
对一个测试用例环境的搭建和销毁,就是一个Fixture,通过覆盖TestCase的setUp()和tearDown()方法来实现。
作用:例如:测试过程中需要访问数据库,那么可以在setUp()中通过建立数据库连接来进行初始化,在tearDown()中清除数据库产生的数据,然后关闭连接。
ps:tearDown的过程很重要,要为下一个testcase留一个干净的环境。
例子:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import unittest
from calculator import Count class TestCount(unittest.TestCase): def setUp(self):
print("test start") def test_add(self):
j = Count(2,3);
print("执行了111")
self.assertEqual(j.add(),5) def test_add2(self):
print("执行了222")
j = Count(12,89)
self.assertEqual(j.add(),101) def tearDown(self):
print("test end ") if __name__ == '__main__': #构造测试集
suite = unittest.TestSuite();
suite.addTest(TestCount("test_add"));
#执行测试
runner = unittest.TextTestRunner();
runner.run(suite)
二、断言方法
assertEqual(first,second,msg=None)
断言第一个参数和第二个参数是否相等,如果不相等则测试失败。msg为可选参数,用于定义测试失败打印的信息。
例1:
assertEqual()比较两个参数是否相等
#!/usr/bin/env python
# -*- coding:utf-8 -*- import unittest class TestCase(unittest.TestCase):
def setUp(self):
self.number = 12
self.number = int(self.number); def test_case(self):
# 判断number是否等于10,且自定义错误时输出信息
self.assertEqual(self.number,10,msg="输入值不等于10"); def tearDown(self):
pass if __name__ == '__main__':
unittest.main()
assertTure()断言方法
判断是否为质数
#!/usr/bin/env python
# -*- coding:utf-8 -*- def is_prime(n):
if n <= 1:
return False;
# 从2开始,质数只能被1和本身整除
for i in range(2,n):
if n % i == 0:
return True
is_prime(10)
test3.py
#!/usr/bin/env python
# -*- coding:utf-8 -*- import unittest
from count import is_prime class TestCase(unittest.TestCase):
def setUp(self):
print("test start")
def test_case(self):
# 返回值是否为True
self.assertTrue(is_prime(10),msg="不是质数") def tearDown(self):
print("test end")
if __name__ == '__main__':
unittest.main()
assertIs()断言
#!/usr/bin/env python
# -*- coding:utf-8 -*- import unittest
from count import is_prime class TestCase(unittest.TestCase):
def setUp(self):
print("test start")
self.a = "hello"
self.b = "hello word"
def test_case(self):
# 返回值是否为True
a = self.a
b = self.b
self.assertIn(a,b,msg="b字符串中没有包含了字符串a") def tearDown(self):
print("test end")
if __name__ == '__main__':
unittest.main()
三、组织单元测试用例
1)当我们添加被测功能和相应的用例后,setUp()和tearDown()方法中所做的事情是每个用例都一样时:
a、另写一个Mytest类封装setUp()和tearDown()方法
b、让测试类继承Mytest类;
calculator.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'tian'
__data__ = '2020/3/26 11:57' class Count:
def __init__(self,a,b):
self.a = int(a)
self.b = int(b) def add(self): #加法计算
return self.a + self.b def sub(self):#减法
return self.a - self.b
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import unittest
from calculator import Count
class Mytest(unittest.TestCase):
def setUp(self):
print("test case start") def tearDown(self):
print("test case end")
# 继承Mytest类
class TestAdd(Mytest): def test_add(self):
j = Count(2,3)
print("执行了111")
self.assertEqual(j.add(),5) def test_add2(self):
print("执行了222")
j = Count(12,89)
self.assertEqual(j.add(),101)
# 继承Mytest类
class TestSub(Mytest): def test_sub(self):
j = Count(2,3)
self.assertEqual(j.sub(),-1) def test_sub1(self):
j = Count(71,46);
self.assertEqual(j.sub(),25) if __name__ == '__main__':
#构造测试集
suite = unittest.TestSuite()
suite.addTest(TestAdd("test_add"))
suite.addTest(TestAdd("test_add2"))
suite.addTest(TestSub("test_sub"))
suite.addTest(TestSub("test_sub1")) #执行测试
runner = unittest.TextTestRunner()
runner.run(suite)
discover更多测试用例
当单元测试用例到达上百个时,如果把所有的测试用例都写在一个test.py中,这个文件会越来越臃肿,后期维护起来很麻烦。需要将这些用例按照所测试的功能进行拆分,分散到不同的测试文件中。
例:
目录结构
testpro/
------- calculator.py
------- runtest.py
------- testadd.py
------- testsub.py
calculator.py
#!/usr/bin/env python
# -*- coding:utf-8 -*- # 计算器类 class Count(): def __init__(self,a,b):
self.a = int(a)
self.b = int(b) # 计算加法
def add(self):
return self.a + self.b def add2(self):
return self.a + self.b ; #计算减法
def sub(self):
return self.a - self.b
testadd.py
#!/usr/bin/env python
# -*- coding:utf-8 -*- from calculator import Count
import unittest class TestAdd(unittest.TestCase): def setUp(self):
print("test case start") def tearDown(self):
print("test case end") def test_add(self):
j = Count(2,3)
self.assertEqual(j.add(),5) def test_add2(self):
j = Count(20,31);
self.assertEqual(j.add(),51) if __name__ == '__main__':
unittest.main()
testsub.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import unittest
from calculator import Count class TestSub(unittest.TestCase): def setUp(self):
print("test case start")
def tearDown(self):
print("test case end")
def test_sub(self):
j = Count(2,3)
self.assertEqual(j.sub(),-1)
def test_sub2(self):
j = Count(71,46)
self.assertEqual(j.sub(),25) if __name__ == '__main__':
unittest.main()
runtest.py
#!/usr/bin/env python
# -*- coding:utf-8 -*- import unittest # 加载测试文件
import testadd
import testsub # 构造测试集
# suite = unittest.TestSuite();
#
# suite.addTest(testadd.TestAdd("test_add"));
# suite.addTest(testadd.TestAdd("test_add2"));
#
#
# suite.addTest(testsub.TestSub("test_sub")) ;
# suite.addTest(testsub.TestSub("test_sub2")); # TestLoader该类负责根据各种标准加载测试用例,并将它们返回给测试套件。
# discover(start_dir,pattern='test*.py',top_level_dir=None)
# start_dir:要测试的模块名或测试用例目录;
# pattern='test*.py': 表用例文件名匹配原则,此处匹配文件名以"test"开头的".py"类型的文件,星号*表示任意多个字符;
# top_level_dir = None:测试模块的顶层目录,如果没有顶层目录,默认为None。 # 定义测试用例的目录为当前目录
test_dir ='./' discover = unittest.defaultTestLoader.discover(test_dir,pattern='test*.py')
if __name__ == '__main__': # 执行测试
runner = unittest.TextTestRunner()
runner.run(discover)
PS:
- 拆分后带来的好处,可以根据不同的功能创建不同的测试文件,甚至是不同的测试目录,测试文件中还可以将不同的小功能划分不同的测试类,在类下编写测试用例,整体结构更加清晰。
- 但是依然没有解决添加测试用例问题,addTest()添加/删除测试用例非常麻烦,所有使用TestLoader类中提供的discover()方法可以解决这个问题;
TestLoader
该类负责根据各种标准加载测试用例,并将它们返回给测试套件,正常情况下,不需要创建这个类的实例。unittest提供了可以共享的defaultTestLoader类,可以使用其子类和方法创建实例,discover()方法就是其中之一:
discover(start_dir,pattern='test*.py',top_level_dir=None)
start_dir:要测试的模块名或测试用例目录;
pattern='test*.py': 表用例文件名匹配原则,此处匹配文件名以"test"开头的".py"类型的文件,星号*表示任意多个字符;
top_level_dir = None:测试模块的顶层目录,如果没有顶层目录,默认为None。
四、关于unittest还需要知道
1、用例执行的顺序
用例的执行顺序涉及多个层次: 在多个测试目录的情况下,先执行哪个目录?在多个测试文件的情况下,先执行哪个文件?在多个测试类的情况下,先执行哪个测试类?在多个测试方法(用例)的情况下,先执行哪个测试方法?
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author:Tian
import unittest; class TestBdd(unittest.TestCase):
def setUp(self):
print("test TestBdd")
def test_ccc(self):
print("test_ccc") def test_aaa(self):
print("test_aaa") def tearDown(self):
pass class TestAdd(unittest.TestCase): def setUp(self):
print("test TestAdd")
def test_bbb(self):
print("test bbb")
def tearDown(self):
pass if __name__ == '__main__':
unittest.main()
输出结果:
test TestAdd
test bbb
test TestBdd
test_aaa
test TestBdd
test_ccc
无论执行多少次,结果都一样,通过上面的结果可以发现,unittest框架默认根据ASCII码的顺序加载测试用例,数字与字母的顺序为:0-9,A~Z,a~z。所以,TestAdd类会优先于TestBdd类执行,test_aaa()方法会优先于test_ccc()被执行,因而它并没有按照用例从上到下的顺序执行。
对于测试目录与测试文件来说,unittest框架同样是按照这个规则来加载测试用例的。
所以我们只能通过测试用例的命名来提高被执行的优先级。
2、跳过测试和预期失败
- unittest.skip(reason) 无条件地跳过装饰的测试,说明跳过测试的原因;
- unittest.skipIf(condition,reason) 跳过装饰的测试,如果条件为真;
- unittest.skipUnless(condition,reason) 跳过装饰的测试,除非条件为真;
- unittest.expectedFailure() 测试标记为失败。不管执行结果是否失败,统一标记为失败。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author:Tian
import unittest
class MyTest(unittest.TestCase): def setUp(self):
pass
def tearDown(self):
pass
@unittest.skip('直接跳过')
def test_skip(self):
print("test aaa")
@unittest.skipIf(3>2,"当条件为True时跳过测试")
def test_skipIf(self):
print("test bbb")
@unittest.skipUnless(3>2,"当条件为True时执行测试")
def test_skip_unless(self):
print("test ccc")
@unittest.expectedFailure #不管执行结果是否失败,统一标记为失败,但不会抛出错误信息。
def test_expected_failure(self):
self.assertEqual(2,3) if __name__ == '__main__':
unittest.main();
fixtures
fixtures可以形象地把它看作是夹心饼干外层的两片饼干,这两片饼干就是setUp/tearDown,中间的心就是测试用例。除此之外,unittest还提供了更大范围的fixtures,例如对于测试类和模块的fixtures。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author:Tian
import unittest def setUpModule():
print("test module start >>>>>>>>>>>>") def tearDownModule():
print("test module end>>>>>>>>>>>>>>") class Test(unittest.TestCase): @classmethod
def setUpClass(cls):
print("test class start==========>")
@classmethod
def tearDownClass(cls):
print("test class end===========>") def setUp(self):
print("test case start --->") def tearDown(self):
print("test case end--->") def test_case(self):
print("test case1") def test_case2(self):
print("test case2") if __name__ == '__main__':
unittest.main() 输出:
test module start >>>>>>>>>>>>
test class start==========>
test case start --->
test case1
test case end--->
test case start --->
test case2
test case end--->
test class end===========>
test module end>>>>>>>>>>>>>>
setUpModule/tearDownModule : 在整个模块的开始与结束的时候执行。
setUpClass/tearDownClass : 在测试类的开始与结束时被执行。
setUp/tearDown :在测试用例的开始与结束时被执行。
需要注意的是,setUpClass/tearDownClass 的写法稍微有些不同。首先,需要通过@classmethod进行装饰,其次方法的参数为cls。其实,cls与self并没有什么特别之处,都只表示类方法的第一个参数。
编写Web测试用例
测试目录:
test_project/
|---------test_casl/
| |------test_baidu.py
| |------test_youdao.py
|---------report/
| |------login.txt
|---------runtest.py
test_baidu.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author:Tian from selenium import webdriver; import unittest,time class MyTest(unittest.TestCase): def setUp(self): self.driver = webdriver.Ie()
self.driver.maximize_window()
self.driver.implicitly_wait(10)
self.base_url ="https://www.baidu.com" def test_baidu(self):
driver = self.driver
driver.get(self.base_url)
driver.find_element_by_xpath(".//*[@id='kw']").clear()
driver.find_element_by_xpath(".//*[@id='kw']").send_keys("unittest")
driver.find_element_by_xpath(".//*[@id='su']").click()
time.sleep(2)
title = driver.title
self.assertEqual(title,"unittest_百度搜索") def tearDown(self):
self.driver.quit() if __name__ == '__main__':
unittest.main()
test_sougo.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author:Tian
from selenium import webdriver;
import unittest,time; class MyTest(unittest.TestCase): def setUp(self):
self.driver = webdriver.Ie()
self.driver.maximize_window()
self.driver.implicitly_wait(10)
self.base_url = "https://www.sogou.com/" def test_youdao(self):
driver = self.driver
driver.get(self.base_url)
driver.find_element_by_xpath(".//*[@id='query']").clear()
driver.find_element_by_xpath(".//*[@id='query']").send_keys("webdriver")
driver.find_element_by_xpath(".//*[@id='stb']").click()
time.sleep(2)
title = driver.title
self.assertEqual(title,"webdriver - 搜狗搜索") def tearDown(self):
self.driver.close() if __name__ == '__main__':
unittest.main()
runntest.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author:Tian import unittest
from HTMLTestRunner_2 import HTMLTestRunne
# 定义测试用例的目录为当前目录 test_dir ="./";
discover = unittest.defaultTestLoader.discover(test_dir,pattern="test*.py") if __name__ == '__main__':
runner = unittest.TestSuite()
runner.addTest(discover)
# 报告存放路径
fp = open('result.html','wb')
# # 定义测试报告
runner = HTMLTestRunner(stream=fp,title='百度搜索测试报告',description='用例执行情况')
runner.run(discover); #运行测试用例
fp.close();
unittest单元测试框架的更多相关文章
- unittest单元测试框架总结
unittest单元测试框架不仅可以适用于单元测试,还可以适用WEB自动化测试用例的开发与执行,该测试框架可组织执行测试用例,并且提供了丰富的断言方法,判断测试用例是否通过,最终生成测试结果.今天笔者 ...
- unittest单元测试框架详解
unittest单元测试框架不仅可以适用于单元测试,还可以适用WEB自动化测试用例的开发与执行,该测试框架可组织执行测试用例,并且提供了丰富的断言方法,判断测试用例是否通过,最终生成测试结果.今天笔者 ...
- Selenium+Python ---- 免登录、等待、unittest单元测试框架、PO模型
1.免登录在进行测试的过程中难免会遇到登录的情况,给测试工作添加了工作量,本文仅提供一些思路供参考解决方式:手动请求中添加cookies.火狐的profile文件记录信息实现.人工介入.万能验证码.去 ...
- unittest单元测试框架简单说明
unittest单元测试框架不仅可以适用于单元测试,还可以适用WEB自动化测试用例的开发与执行,该测试框架可组织执行测试用例,并且提供了丰富的断言方法,判断测试用例是否通过,最终生成测试结果.今天笔者 ...
- Python+selenium之简单介绍unittest单元测试框架
Python+selenium之简单介绍unittest单元测试框架 一.unittest简单介绍 unittest支持测试自动化,共享测试用例中的初始化和关闭退出代码,在unittest中最小单元是 ...
- unittest单元测试框架总结(转载)
转载:https://www.cnblogs.com/yufeihlf/p/5707929.html unittest单元测试框架不仅可以适用于单元测试,还可以适用WEB自动化测试用例的开发与执行,该 ...
- 四. 引入unittest单元测试框架
1. 安装 SeleniumIDE(firefox) (1)下载地址:https://addons.mozilla.org/en-US/firefox/addon/selenium-ide/ (2 ...
- unittest 单元测试框架断言方法
unittest单元测试框架的TestCase类下,测试结果断言方法:Assertion methods 方法 检查 版本 assertEqual(a, b) a == b assertNotEqu ...
- unittest 单元测试框架
引入 unittest 框架 相想使用unittest 框架,首先要引入unittest 包 import unittest class Baidu(unittest.TestCase): Baidu ...
随机推荐
- 关于get和post请求的区别
1.标准答案 GET在浏览器回退时是无害的,而POST会再次提交请求. GET产生的URL地址可以被Bookmark,而POST不可以. GET请求会被浏览器主动cache,而POST不会,除非手动设 ...
- windows环境下wamp安装redis拓展
环境: wamp集成环境 安装分为两部 1.安装redis客户端 https://github.com/ServiceStack/redis-windows/raw/master/download ...
- Java: 集合类详解
0.参考文献 http://blog.csdn.net/liulin_good/article/details/6213815 1.java集合类图 1.1 1.2 上述类图中,实线边框的是实现类,比 ...
- final,finally,finalize
final:可以修饰属性,可以修饰方法(方法不能被重写,可以继承),可以修饰类(该类不能被继承,不能产生子类) finally:无论什么情况,都会执行 finalize:垃圾回收时,调用此方法
- Unity打包提示UnityEditor.BuildPlayerWindow+BuildMethodException: Build failed with errors.错误
不要将打包的输出路径设置为Assets文件夹下面即可,MD真坑 老外给出的解释: As you have noticed after you click build settings you are ...
- mongdb的索引及备份
1. mongodb的索引 1.1 为什么mongdb需要创建索引 加快查询速度 进行数据的去重 1.2 mongodb创建简单的索引方法 语法: db.集合.ensureIndex({属性:1}), ...
- POM文件详解(1)
POM文件详解 <project xmlns=http://maven.apache.org/POM/4.0.0 xmlns:xsi="http://www.w3.org/2001/X ...
- 2.Spring 拦截器应用
首先咱们来了解一下具体的业务场景(这个跟第一篇中的很相似但有不同):具体的业务是这样的,现在系统中有六十多个主档(功能模块),每个主档都有新增.修改.删除功能,当我们在对每个主档做这些操作时需要对其记 ...
- [转] The QCOW2 Image Format
The QCOW2 Image Format https://people.gnome.org/~markmc/qcow-image-format.html The QCOW image format ...
- Docker应用:Docker-compose(容器编排)
阅读目录: Docker应用:Hello World Docker应用:Docker-compose(容器编排) 前言: 昨天完成了Docker入门示例(Docker应用:Hello World),示 ...