Python基础-测试和打包

主要介绍测试和打包相关内容与命令

测试相关
文档
  • 查看文档命令 python -m pydoc 模块名
  • 生成html文档 python -m pydoc -w 模块名
  • 启动服务查看文档 python -m pydoc -p 端口号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
"""
测试文档的模块
主要用于测试生成文档
"""

MY_NAME = '光辉岁月'


def say_hi(name):
'''
定义一个打招呼的函数
返回指定用户打招呼的字符串
'''
print('执行say_hi函数')
return name + '你好'


def print_rect(height, width):
'''
定义一个打印矩形的函数
height - 代表矩形的高度
width - 代表矩形的宽
'''
print(('*' * width + '\n') * height)


class User:
NATIONAL = 'China'
"""
定义一个代表用户的类
该类包括name,age两个变量
"""

def __init__(self, name, age):
"""
name 初始化该用户的name
age 初始化该用户的age
"""
self.name = name
self.age = age

def eat(self, food):
"""
定义用户吃东西的方法
food — 代表用户正在吃的东西
"""
print('%s正在吃%s' % (self.name, food))


# python -m pydoc -w 模块名 生成html文档

使用上面的命令可以看到上面的例子的文档

文档测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18


def square(x):
"""
计算平方的函数
例如:
>>> square(2)
4
>>> square(3)
9
"""
return x * 2


if __name__ == '__main__':
"""文档测试"""
import doctest
doctest.testmod()

在注释中按照上面的格式书写 然后运行doctest.testmod() 会自动测试注释中的代码

单元测试 PyUnit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
"""
PyUnit(unittest)
"""


def one_equation(a, b):
"""
求一元一次方程a*x+b=0的解
"""
if a == 0:
raise ValueError('参数错误')
else:
return -b / a
#return b / a


def two_equation(a, b, c):
"""
求一元二次方程a*x*x+b*x+c=0的解
"""
if a == 0:
raise ValueError('参数错误')
elif b * b - 4 * a * c < 0:
raise ValueError('方程再有理方位内无解')
elif b * b - 4 * a * c == 0:
return -b / (2 * a)
else:
r1 = (-b + (b * b - 4 * a * c)**0.5) / 2 / a
r2 = (-b - (b * b - 4 * a * c)**0.5) / 2 / a
return r1, r2

上面是待测试模块,下面用PyUnit进行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
"""
测试类 继承unittest.TestCase类
方法需无返回值 无参数 以test_开头
文件名也用test_开头
"""
from math03 import *
import unittest


class TestMath(unittest.TestCase):

def test_one(self):
self.assertEqual(one_equation(5, 9), -1.8)
self.assertTrue(one_equation(4, 10) == -2.5, .00001)
self.assertTrue(one_equation(4, -27) == 27 / 4)
with self.assertRaises(ValueError):
one_equation(0, 9)

def test_two(self):
r1, r2 = two_equation(1, -3, 2)
self.assertCountEqual((r1, r2), (1.0, 2.0), '求解出错')
r1, r2 = two_equation(2, -7, 6)
self.assertCountEqual((r1, r2), (1.5, 2.0), '求解出错')
r = two_equation(1, -4, 4)
self.assertEqual(r, 2.0, '求解出错')
with self.assertRaises(ValueError):
two_equation(0, 9, 3)
with self.assertRaises(ValueError):
two_equation(4, 2, 3)


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

# 或者 python -m unittest 【模块名】
测试包和前后执行
1
2
3
4
5
6
def say_hello():
return 'hello world'


def add(nA, nB):
return nA + nB

上面是测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import unittest
from hello04 import *


class TestHello(unittest.TestCase):

def test_say_hello(self):
self.skipTest('临时跳过test_say_hello')
self.assertEqual(say_hello(), 'hello world')

# @unittest.skip('临时跳过test_add')
def test_add(self):
self.assertEqual(add(3, 4), 7)
self.assertEqual(add(0, 4), 4)
self.assertEqual(add(-3, 0), -3)

# 类级别前后执行
@classmethod
def setUpClass(cls):
print("===============setUpClass==========")

@classmethod
def tearDownClass(cls):
print("===========tearDown==============")

# 测试用例前后执行
def setUp(self):
print("========初始化setUp()=============")

def tearDown(self):
print("=======tearDown() 销毁======")


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

上面的类标注是表示类前后执行,只执行一次,而测试用例setUp和tearDown会在每个测试用例前后执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
"""
TestSuite 测试包
"""
import unittest
from test_math04 import TestMath
from test_hello05 import TestHello

test_cases = (TestMath, TestHello)


def whole_suite():
loader = unittest.TestLoader()
suite = unittest.TestSuite()

for test_class in test_cases:
tests = loader.loadTestsFromTestCase(test_class)
suite.addTests(tests)
return suite


if __name__ == '__main__':
# runner = unittest.TextTestRunner(verbosity=2)
# runner.run(whole_suite())
# 生成文件
with open('test_report.txt', 'a') as f:
runner = unittest.TextTestRunner(verbosity=2, stream=f)
runner.run(whole_suite())

上面是测试包的用法,把几个测试用例组织一起,可以生产报告到文件


打包相关
zipapp进行打包

同一个目录建立文件hello.py 和 app.py,其中app.py引入hello.py的函数

1
2
def say_hello(name):
return name + "你好"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from hello import *

"""
zipapp 模块进行打包
-o 输出档案包的名 默认zipapp命令后面的 文件夹名.pyz
-m 入口函数 值形式为pkg.mod:fn
-c 3.7之后支持 是否压缩
"""


def main():
print('程序开始执行')
print(say_hello('pyhon'))

# 返回当前文件所在文件夹的父级目录
# python -m zipapp 当前文件所在文件夹 -o 文件名.pyz -m "app:main"
# 打包完成后执行 python 文件名.pyz

返回上一级目录 python -m zipapp 包含app的目录名 -o first.pyz -m “app:main” ,然后就看到first.pyz

生产了 然后用python first.pyz 运行


如果有第三方包怎么弄呢,下面演示下

  1. 新建dbapp目录

  2. 进入dbapp目录 新建文件exec_select.py 和__main__.py,内容如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import mysql.connector


    def query_user():

    conn = mysql.connector.connect(user='root', password='root',
    host='localhost', port='3306',
    database='python', use_unicode=True)

    c = conn.cursor()

    c.execute('select * from user_tb')

    for row in c:
    print(row)

    c.close()
    conn.close()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from exec_select import *

    query_user()



    '''
    1. 安装依赖模块 到上级目录 python -m pip install -r requirement.txt --target 当前文件所在目录
    requirement.txt 为需要安装的模块名
    2.如果安装包有.dist-info 目录建议删除
    3.python -m zipapp xxx 由于有__main__.py文件 不用-m

    '''
  3. 返回上级目录新建requirements.txt 里面加入依赖模块名mysql-connector-python

  4. 执行安装命令 python -m pip install -r requirements.txt –target dbapp
  5. 打包 python -m zipapp dbapp
PyInstaller 打包

需要先安装 pip install pyinstaller

将上面的app.py改成可执行的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from hello import *

"""
pyinstaller
当前文件目录执行 pyinstaller -F app.py
-F 生成exe -D 生成目录执行文件
"""


def main():
print('程序开始执行')
print(say_hello('pyhon'))


if __name__ == '__main__':
main()

当前目录执行pyinstaller -F app.py 就能生成exe文件了 ,而pyinstaller -D app.py生成带目录的执行文件