Python Nose 自动化测试框架介绍
文章目录
1. unittest 简介
1.1 python 单元测试
1、python语言非常简洁和方便,一个最简单的程序 hello.py 编写如下:
class hello():
def __init__(self):
print("class hello init!")
if __name__ == "__main__":
h = hello()
运行结果:
# python hello.py
class hello init!
2、我们使用 python 进一步开发了一些功能函数,如 demo.py
文件:
#!/usr/bin/python
# -*- coding: utf-8 -*-
def add(a, b):
return a+b
def minus(a, b):
return a-b
如果要对这些功能函数进行单元测试,通常需要开发一系列的测试代码来进行验证:
def test_add(self):
"""Test method add(a, b)"""
self.assertEqual(3, add(1, 2))
self.assertNotEqual(3, add(2, 2))
def test_minus(self):
"""Test method minus(a, b)"""
self.assertEqual(1, minus(3, 2))
self.assertNotEqual(1, minus(3, 2))
一切看起来都很ok,但是如果我们开发了成百上千的测试代码以后,如何调用这些 test_xxx()
测试代码成了一个难题。我们不得不再开发一套框架来调用这些测试用例,幸运的是 pythonn 已经内建了这样一套框架 unittest
。
1.2 unittest 测试框架
unittest 的运行流程如上图所示:
- 1、unittest 首先分析
被测对象
,找出其中的TestCase
和TestFixture
,被由TestLoader
将其打包成TestSuite
。 - 2、然后在
TestRunner
中逐个拆包TestSuite
,并按照运行运行其中的TestCase
和TestFixture
。 - 3、测试结果由
TestReporter
输出成txt/html/xml
等格式以供分析。
这里面有一些核心概念,逐一澄清:
- 1、
被测对象
。unittest
把程序中所有继承了unittest.TestCase
类 的子类,看成被测对象。而nosetests
扩充了被测对象,文件夹、文件、模块、类、函数 都可以充当被测对象。
import unittest
class Abcd(unittest.TestCase):
...
- 2、
TestCase
。被测对象
中所有以test
开头的函数都被认为是TestCase
,会被调用。
import unittest
class Abcd(unittest.TestCase):
def test1xxx(self):
...
def test2xxx(self):
...
- 3、
TestFixture
。字面意思是测试夹具
很形象的表示了它的作用,在调用TestCase
之前需要准备测试环境,在调用TestCase
之后需要恢复环境。在被测对象
中需要使用一些关键字来定义TestFixture
函数。
import unittest
class Abcd(unittest.TestCase):
@classmethod
def setUpClass(cls):
...
@classmethod
def tearDownClass(cls):
...
def setUp(self):
...
def tearDown(self):
...
def test1xxx(self):
...
def test2xxx(self):
...
# setUp():准备环境,执行每个测试用例的前置条件
# tearDown():环境还原,执行每个测试用例的后置条件
# setUpClass():必须使用@classmethod装饰器,所有case执行的前置条件,只运行一次
# tearDownClass():必须使用@classmethod装饰器,所有case运行完后只运行一次
- 4、
TestLoader
。负责将TestCase
和TestFixture
打包成TestSuite
。可以使用unittest
默认的打包方式,也可以手工进行自定义打包。 - 5、
TestRunner
。负责按照对于的顺序来运行TestCase
和TestFixture
。通常情况下使用unittest.main()
来启动整个unittest
,也可以手工定制。
import unittest
unittest.main()
- 6、
TestReporter
。负责将测试结果输出成txt/html/xml
等格式。
1.3 默认模式
我们使用一个实例了来理解上述的概念,编写一个 test_demo_class.py
文件来测试 demo.py
中的函数:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import unittest
from demo import add, minus
class TestDemo(unittest.TestCase):
"""Test mathfuc.py"""
@classmethod
def setUpClass(cls):
print ("this setupclass() method only called once.\n")
@classmethod
def tearDownClass(cls):
print ("this teardownclass() method only called once too.\n")
def setUp(self):
print ("do something before test : prepare environment.\n")
def tearDown(self):
print ("do something after test : clean up.\n")
def test_add(self):
"""Test method add(a, b)"""
self.assertEqual(3, add(1, 2))
self.assertNotEqual(3, add(2, 2))
def test_minus(self):
"""Test method minus(a, b)"""
self.assertEqual(1, minus(3, 2))
self.assertNotEqual(1, minus(3, 2))
@unittest.skip("do't run as not ready")
def test_minus_with_skip(self):
"""Test method minus(a, b)"""
self.assertEqual(1, minus(3, 2))
self.assertNotEqual(1, minus(3, 2))
if __name__ == '__main__':
# verbosity=*:默认是1;设为0,则不输出每一个用例的执行结果;2-输出详细的执行结果
unittest.main(verbosity=1)
运行结果:
# python test_demo_class.py
this setupclass() method only called once.
do something before test : prepare environment.
do something after test : clean up.
.do something before test : prepare environment.
do something after test : clean up.
Fsthis teardownclass() method only called once too.
======================================================================
FAIL: test_minus (__main__.TestDemo)
Test method minus(a, b)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\weilin.peng\Documents\python\test_demo_class.py", line 33, in test_minus
self.assertNotEqual(1, minus(3, 2))
AssertionError: 1 == 1
----------------------------------------------------------------------
Ran 3 tests in 0.002s
FAILED (failures=1, skipped=1)
1.4 手工模式
在上述的实例中,只要调用 unittest.main()
,整个 TestLoader
打包 TestSuite
、TestRunner
运行 TestSuite
、TestReporter
输出测试结果 都是自动进行的。
当然你也可以定制这一切,以下是一个手工定制来调用 test_demo_class.py
中测试的例子。本文件为 test_demo_module.py
:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import HTMLReport
import unittest
import test_demo_class
from test_demo_class import TestDemo
if __name__ == '__main__':
paras = sys.argv[1:]
args = paras[0]
report = paras[1]
suite = unittest.TestSuite()
if args == 'test':
tests = [TestDemo("test_minus"), TestDemo("test_add"), TestDemo("test_minus_with_skip")]
suite.addTests(tests)
elif args == 'tests':
suite.addTest(TestDemo("test_minus"))
suite.addTest(TestDemo("test_add"))
suite.addTest(TestDemo("test_minus_with_skip"))
elif args == 'class':
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestDemo))
elif args == 'module':
suite.addTests(unittest.TestLoader().loadTestsFromModule(test_demo_class))
elif args == 'mix':
suite.addTests(unittest.TestLoader().loadTestsFromName('test_demo_class.TestDemo.test_minus'))
elif args == 'mixs':
suite.addTests(unittest.TestLoader().loadTestsFromNames(['test_demo_class.TestDemo.test_minus', 'test_demo_class.TestDemo', 'test_demo_class']))
elif args == 'discover':
suite.addTests(unittest.TestLoader().discover('.', 'test_*.py', top_level_dir=None))
if report == 'terminal':
runner = unittest.TextTestRunner(verbosity=1)
runner.run(suite)
elif report == 'txt':
with open('ut_log.txt', 'a') as fp:
runner = unittest.TextTestRunner(stream=fp, verbosity=1)
runner.run(suite)
elif report == 'html':
runner = HTMLReport.TestRunner(report_file_name='test',
output_path='report',
title='测试报告',
description='测试描述',
sequential_execution=True
)
runner.run(suite)
运行结果:
# python test_demo_module.py test txt
this setupclass() method only called once.
do something before test : prepare environment.
do something after test : clean up.
do something before test : prepare environment.
do something after test : clean up.
this teardownclass() method only called once too.
2. nose 扩展框架
得益于 unittest
优秀的架构,有几个框架在此之上进行了扩展,例如 nose
和 pytest
。
nose
的口号是 nose extends unittest to make testing easier.
,可见它是基于 unittest
之上进行的扩展,主体思想是和 unittest
一脉相承的,各个术语也是通用的。值得一提的话 nose 1
已经停止了维护,但是因为功能非常强大和好用应对一般的测试已经绰绰有余了,可以继续使用。如果介意的话,可以转用 pytest
。
2.1 nose
的安装和基本用法
使用 pip 安装 nose:
pip install nose
最简单的使用方法,进入工程目录执行 nosetests
命令,nose
会自动搜寻工程目录下的 TestCase
并执行:
cd path/to/project
nosetests
2.2 被测对象
的扩展
nose
一个最大的特色就是对 unittest
的 被测对象
进行了扩展,极大的方便了测试。可以在不指定具体被测对象的情况下,nose
根据一定规则能找出工程中绝大部分的 TestCase
。
nose
根据以下的规则来自动查找 被测对象
:
1、If it looks like a test, it’s a test. Names of directories, modules, classes and functions are compared against the testMatch regular expression, and those that match are considered tests. Any class that is a unittest.TestCase subclass is also collected, so long as it is inside of a module that looks like a test.
1、如果它看起来像一个
test
,那么它就是一个test
。文件夹
、模块 (modules)
、类 (claesses)
、函数(functions)
的名字 和testMatch
正则表达进行比较,匹配即视为tests
。另外任何unittest.TestCase
的子类也会被搜集,只要它在一个看起来像测试的模块中。2、Files with the executable bit set are ignored by default under Unix-style operating systems–use
--exe
to allow collection from them, but be careful that is safe to do so. Under Windows, executable files will be picked up by default since there is no executable bit to test.2、在unix风格的操作系统下,默认情况下会忽略带有可执行位集的文件,使用
--exe
选项来允许从它们收集,但是要注意这样做是安全的。在Windows下,默认情况下将选择可执行文件,因为没有可执行位要测试。3、Directories that don’t look like tests and aren’t packages are not inspected.
3、看起来不像测试或不是
包 (packages)
的目录不会被检查。4、Packages are always inspected, but they are only collected if they look like tests. This means that you can include your tests inside of your packages (somepackage/tests) and nose will collect the tests without running package code inappropriately.
4、
包 (packages)
总是被检查的,但是只有当其中代码看起来像测试时才会被收集。这意味着您可以在包(某些包/测试)中包含测试,nose将收集测试,而不会不适当地运行包代码。5、When a project appears to have library and test code organized into separate directories, library directories are examined first.
5、当一个
项目 (project)
将库和测试代码组织到单独的目录中时,首先检查库目录。6、When nose imports a module, it adds that module’s directory to sys.path; when the module is inside of a package, like package.module, it will be loaded as package.module and the directory of package will be added to sys.path.
6、当
nose
导入一个模块 (module)
时,它会将该模块的目录添加到sys.path
;当模块在包 (packages)
中,比如package.module
,它将作为package.module
加载,package
目录将被添加到sys.path
。7、If an object defines a
__test__
attribute that does not evaluate to True, that object will not be collected, nor will any objects it contains.7、如果一个
对象 (object)
定义了一个__test__
属性的值不为True
,那么该对象和它包含的任何对象都不会被收集。8、Be aware that plugins and command line options can change any of those rules.
8、请注意,插件和命令行选项可以更改任何这些规则。
可以看到 nose
的默认规则极大的扩展了 被测对象
,从 unittest
的一种扩展到以下的几种:
index | target | condition | descript |
---|---|---|---|
0 | 文件夹 (Directory) / 包 (packages) | 条件1:文件夹 name 匹配 testMatch 条件2:文件夹包含 __init__.py 是一个包 (packages) |
满足任一条件即可 |
1 | 文件 (File) / 模块 (Module) | 条件1:文件 name 匹配 testMatch |
- |
2 | 类 (claesses) | 条件1:类 name 匹配 testMatch 条件2:类继承了 unittest.TestCase 父类 |
满足任一条件即可 条件2是unittest唯一支持的 |
3 | 函数 (functions) | 条件1:函数 name 匹配 testMatch |
- |
这里面的一个关键就是 testMatch
正则表达式,nosetests --help
会打印出 testMatch
的默认值:
-m=REGEX, --match=REGEX, --testmatch=REGEX¶
Files, directories, function names, and class names that match this regular expression are considered tests. Default: (?:\b|_)[Tt]est [NOSE_TESTMATCH]
可以看到testMatch
的默认值 为 (?:\b|_)[Tt]est
,解析正则表达式的具体含义:
2.3 TestFixture
的扩展
nose
支持各个层级的 TestFixture
,在每个层级都能进行 测试场景部署 和 原场景还原 的动作。
index | target | fixture keyword | descript |
---|---|---|---|
0 | Test packages | setup()/setup_package()/setUp()/setUpPackage() teardown()/teardown_package()/tearDown()/tearDownPackage() |
在测试包/文件夹中的 __init__.py 文件中定义 |
1 | Test modules | setup()/setup_module()/setUp()/setUpModule() teardown()/teardown_module()/tearDownModule() |
- |
2 | Test classes | setup_class()/setupClass()/setUpClass()/setupAll()/setUpAll() teardown_class()/teardownClass()/tearDownClass()/teardownAll()/tearDownAll() |
必须使用 @classmethod 声明成类方法 |
3 | Test functions | setup()/setup_func() teardown()/teardown_func() |
可以使用类似方法给函数分配 fixture 函数test = with_setup(setup_func, teardown_func)(test) |
以下是一个基本的例子来展示不同层次 TestFixture
的不同执行时机。
- 1、unnecessary_math.py:
'''
Module showing how doctests can be included with source code
Each '>>>' line is run as if in a python shell, and counts as a test.
The next line, if not '>>>' is the expected output of the previous line.
If anything doesn't match exactly (including trailing spaces), the test fails.
'''
def multiply(a, b):
"""
>>> multiply(4, 3)
12
>>> multiply('a', 3)
'aaa'
"""
return a * b
- 2、test_um_nose_fixtures.py:
from nose import with_setup # optional
from unnecessary_math import multiply
def setup_module(module):
print ("") # this is to get a newline after the dots
print ("setup_module before anything in this file")
def teardown_module(module):
print ("teardown_module after everything in this file")
def my_setup_function():
print ("my_setup_function")
def my_teardown_function():
print ("my_teardown_function")
@with_setup(my_setup_function, my_teardown_function)
def test_numbers_3_4():
print ('test_numbers_3_4 <============================ actual test code')
assert multiply(3,4) == 12
@with_setup(my_setup_function, my_teardown_function)
def test_strings_a_3():
print ('test_strings_a_3 <============================ actual test code')
assert multiply('a',3) == 'aaa'
class TestUM:
@classmethod
def setup_class(cls):
print ("setup_class() before any methods in this class")
@classmethod
def teardown_class(cls):
print ("teardown_class() after any methods in this class")
def setup(self):
print ("TestUM:setup() before each test method")
def teardown(self):
print ("TestUM:teardown() after each test method")
def test_numbers_5_6(self):
print ('test_numbers_5_6() <============================ actual test code')
assert multiply(5,6) == 30
def test_strings_b_2(self):
print ('test_strings_b_2() <============================ actual test code')
assert multiply('b',2) == 'bb'
- 3、运行结果:
# nosetests -s test_um_nose_fixtures.py
setup_module before anything in this file
setup_class() before any methods in this class
TestUM:setup() before each test method
test_numbers_5_6() <============================ actual test code
TestUM:teardown() after each test method
.TestUM:setup() before each test method
test_strings_b_2() <============================ actual test code
TestUM:teardown() after each test method
.teardown_class() after any methods in this class
my_setup_function
test_numbers_3_4 <============================ actual test code
my_teardown_function
.my_setup_function
test_strings_a_3 <============================ actual test code
my_teardown_function
.teardown_module after everything in this file
----------------------------------------------------------------------
Ran 4 tests in 0.012s
OK
2.4 nose 插件
nose 还提供了丰富的内部和外部插件,并且可以自己开发插件。来使测试更加智能和方便。
关于这部分可以重点参考以下文档:Believer007 nose 系列文章、nose 1.3.7 documentation
参考资料
1.unittest — Unit testing framework
2.Python 单元测试 - unittest
3.Python 单元测试 - HTML report
4.Python 单元测试 - pytest
5.Believer007 nose 系列文章
6.nose Installation and quick start
7.nose 1.3.7 documentation
8.nose introduction
Python Nose 自动化测试框架介绍的更多相关文章
- python nose测试框架全面介绍十---用例的跳过
又来写nose了,这次主要介绍nose中的用例跳过应用,之前也有介绍,见python nose测试框架全面介绍四,但介绍的不详细.下面详细解析下 nose自带的SkipTest 先看看nose自带的S ...
- python nose测试框架全面介绍七--日志相关
引: 之前使用nose框架时,一直使用--logging-config的log文件来生成日志,具体的log配置可见之前python nose测试框架全面介绍四. 但使用一段时间后,发出一个问题,生成的 ...
- python nose测试框架全面介绍六--框架函数别名
之前python nose测试框架全面介绍二中介绍了nose框架的基本构成,但在实际应该中我们也会到setup_function等一系列的名字,查看管网后,我们罗列下nose框架中函数的别名 1.pa ...
- Python web自动化测试框架搭建(功能&接口)——unittest介绍
Python UnitTest测试框架介绍 1) TestCase:所有测试用例类继承的基本类, TestCase的实例就是测试用例 2) TestSuite:测试套件 ...
- Python BDD自动化测试框架初探
1. 什么是BDD BDD全称Behavior Driven Development,译作"行为驱动开发",是基于TDD (Test Driven Development 测试驱动 ...
- Python接口自动化测试框架实战 从设计到开发
第1章 课程介绍(不要错过)本章主要讲解课程的详细安排.课程学习要求.课程面向用户等,让大家很直观的对课程有整体认知! 第2章 接口测试工具Fiddler的运用本章重点讲解如何抓app\web的htt ...
- 基于Python接口自动化测试框架+数据与代码分离(进阶篇)附源码
引言 在上一篇<基于Python接口自动化测试框架(初级篇)附源码>讲过了接口自动化测试框架的搭建,最核心的模块功能就是测试数据库初始化,再来看看之前的框架结构: 可以看出testcase ...
- Selenium自动化测试框架介绍
Selenium自动化测试框架介绍 1.测试架构作用 a.可维护性 b.提高编写脚本效率 c.提高脚本的可读性 2.框架的几大要素: Driver管理,脚本,数据,元素对象,LOG,报告,运行机制,失 ...
- UiAutomator自动化测试框架介绍
UiAutomator自动化测试框架介绍 环境搭建 1 必要条件 1.1 JDK 1.2 SDK(API高于15) 1.3 Eclipse 2 ...
随机推荐
- Java安全之 ClassLoader类加载器
Java安全之 ClassLoader类加载器 0x00 前言 前面这里抛出一个问题,Java到底是什么类型的编程语言?是编译型?还是解释型?在这个问题是其实一直都都有疑惑,如果说是解释型语言的话,那 ...
- 【大咖直播】Elastic 企业搜索实战工作坊(第一期)
借助 App Search 提供的内置功能,您可轻松打造卓越的搜索体验.直观的相关度调整以及开箱即用的搜索分析,不仅可以优化所提供的内容,其提供的 API 还可帮助您将位于各处的所有内容源关联在一起. ...
- 服务器内部模拟Http请求
前言: 在做小程序的开发时需要获取用户的openId用来做唯一标识,来获取对应用户的相关数据 官方的文档说明上有四个必须传的参数 其中appId和appSecret可在自己的微信公众号平台上获取,同时 ...
- GitHub 和 Gitee 开源免费 10 个超赞后台管理面板,看完惊呆了!
软件工程师在实际项目开发中不可避免需要依赖一些前后端的后台管理系统框架,而不是从零开始一点点的搭建,浪费人力.目前市面上有很多开放源码.且免费的后台管理面板,样式色彩也比较丰富美观. 今天整理 ...
- ASP.NET Core中将Json字符串转换为JsonResult
ASP.NET Core中返回JsonResult 最近在使用NET 5.0做WebApi中,发现只能返回string类型,不能用JsonResult返回实体,于是查阅资料找到解决办法. 两种方式分别 ...
- sqlserver 2000 insert注入的问题
一个sql server 2000的注入点猜测语句如下:insert into t1(col1, col2, col3) values('注入点1','数据点2','xxx');注入点1的值可以通过o ...
- 踩坑系列《四》a标签的href属性拼接问题
如上所示,无法直接在 html里面的 a 标签的href属性传递参数时,只需要在 JS 中获取对应 a 标签的id,再通过 attr 方法抓到 href,进行字符串拼接即可
- 下载cnki硕博士论文的pdf版
每找到一篇心仪的硕博士论文时,总是迫不及待下载到本地吧. 可是接下来你只能选择caj格式. caj界面都用过吧,没用过,你也不会来这. 我就想看pdf版本的,怎么办呢?有办法! 重点来了,敲黑板: 1 ...
- modal框
modal框 创建modal款的基本"框架": 1 <body> 2 <!--1.触发模态框的按钮--> 3 <button class=" ...
- 免费 CDN 玩法 —— 文件一键上传到 NPM
前言 unpkg.jsdelivr 等站点可加速 NPM 包文件,适合作为个人网站或演示案例的免费 CDN. 虽然上传文件到 NPM 很简单,创建 package.json 然后 npm publis ...