2018-08-07(16)单元测试(下)

2018-08-07  本文已影响0人  棕色试剂瓶

Python基础语法(16)

单元测试

测试用例的核心工作原理 : test case(测试用例); test suite(测试套件), test runner(测试运行器); test fixture(测试装备)

代码实例

test_func.py

<pre spellcheck="false" class="md-fences md-end-block contain-cm modeLoaded" lang="python" contenteditable="false" cid="n30" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Consolas, "Liberation Mono", Courier, monospace; font-size: 0.9em; white-space: normal; display: block; break-inside: avoid; text-align: left; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(223, 226, 229); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">def add(a,b):
return a + b

def minus(a,b):
return a - b

def multi(a,b):
return a*b

def divide(a,b):
return a/b</pre>

tese_func.py

<pre spellcheck="false" class="md-fences md-end-block contain-cm modeLoaded" lang="python" contenteditable="false" cid="n33" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Consolas, "Liberation Mono", Courier, monospace; font-size: 0.9em; white-space: normal; display: block; break-inside: avoid; text-align: left; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(223, 226, 229); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">import unittest # 导入unittest模块
from week4.unit1.test_func import *

class TestFunc(unittest.TestCase): # 测试用例
'''test test_func.py'''

每个测试方法均以test开头,否则是不被unittest识别的。

def test_add(self):
'''test method add(a,b)'''
self.assertEqual(3,add(1,2))
self.assertNotEqual(2,add(2,2))
def test_minus(self):
'''test method minus(a,b)'''
self.assertEqual(1,minus(3,2))
def test_multi(self):
'''test method multi(a,b)'''
self.assertEqual(6,multi(2,3))
def test_divide(self):
'''test method divide(a,b)'''
self.assertEqual(2,divide(6,3))
self.assertEqual(2.5, divide(5,2))
if name == "main":
unittest.main()
</pre>

代码中,TestFunc就是一个测试用例,测试用例每有一个test开头的方法,再load的时候便会生成一个TestCase实例。

之后我们通过TestLoader加载TestCase到TestSuite,用TextTestRunner来运行TestSuite。运行结果保存再TestTestResult中,我们通过命令行或者unittest.main()执行时,main会调用到TextTestRunner中的run来执行,

或者我们可以通过直接使用TextTestRunner来执行用例。

再Runner执行时,默认将执行结果说出到控制台,但是我们可以设置输出到其它文件,再文件中查看结果。(HTMLTestRunner,通过它可以将结果输出到HTML中,生成较为漂亮的报告)

TestSuite

通过该方法来控制用例的执行,添加到TestSuite中的case会按照添加的顺序执行。

还可以再Test Suite中添加多个测试文件,

代码实例:

<pre spellcheck="false" class="md-fences md-end-block contain-cm modeLoaded" lang="python" contenteditable="false" cid="n51" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Consolas, "Liberation Mono", Courier, monospace; font-size: 0.9em; white-space: normal; display: block; break-inside: avoid; text-align: left; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(223, 226, 229); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">import unittest
from test_func import TestFunc

if name == "main":
suite = unittest.TestSuite()
tests = [TestFunc("test_add), TestFunc("test_minus"), TestFunc("test_divide")]
suite.addTests(tests)

runner = unittest.TextTestRunner(verbosity = 2)
runner.run(suite)</pre>

通过下面的方法也可以传入测试用例

<pre spellcheck="false" class="md-fences md-end-block contain-cm modeLoaded" lang="python" contenteditable="false" cid="n54" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Consolas, "Liberation Mono", Courier, monospace; font-size: 0.9em; white-space: normal; display: block; break-inside: avoid; text-align: left; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(223, 226, 229); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"># 添加单个TestCase
suite.addTest(TestCalculateFunc("test_multi")) # 注意,是addTest,不是addTests,留神s的区别

通过使用loadTestFromName() 括号内传入"模块名.TestCase名"

suite.addTests(unittest.TestLoader().loadTestsFromName("test_func.TestFunc")) # 添加单个
suite.addTests(unittest.TestLoader().loadTestsFromNames([test_func.TestFunc])) # 添加多,注意s和中括号的差别

通过使用loadTestsFromTestCase() 传入TestCase

suite.addTests(unittest.TestLoader().loadTestFromTestCase(TestFunc))
runner = unittes.TextTestRunner(verbosity = 2) # verbosity = 1 是打印的报告不如2的详细。
runner.run(suite)</pre>

注意: 用TestLoader无法对case进行排序

将测试结果写入文件

可以将测试结果写入文件,生成测试报告,对模块质量提供依据。

代码实例:

<pre spellcheck="false" class="md-fences md-end-block contain-cm modeLoaded" lang="python" contenteditable="false" cid="n65" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Consolas, "Liberation Mono", Courier, monospace; font-size: 0.9em; white-space: normal; display: block; break-inside: avoid; text-align: left; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(223, 226, 229); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">if name == "main":
suit = unittest.TestSuite()
suite.addTests(unittest.TestLoader().loadTestFromTestCase(TestCalculateFunc))
with open("UnittestTestReport.txt","a") as f:
runner = unittest.TextTestRunner(stream = f, verbosity = 2) # 将测试报告写入文件,可以设置verbosity
runner.run(suite)</pre>

unittest中的参数化

首先需要安装nose_parameterized模块 pip instuall nose_parameterized

方法模块:

<pre spellcheck="false" class="md-fences md-end-block contain-cm modeLoaded" lang="python" contenteditable="false" cid="n73" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Consolas, "Liberation Mono", Courier, monospace; font-size: 0.9em; white-space: normal; display: block; break-inside: avoid; text-align: left; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(223, 226, 229); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">def login(username,password):
if username == "Eric" and password == "123456":
return True
else:
return False</pre>

测试套件模块:

<pre spellcheck="false" class="md-fences md-end-block contain-cm modeLoaded" lang="python" contenteditable="false" cid="n76" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Consolas, "Liberation Mono", Courier, monospace; font-size: 0.9em; white-space: normal; display: block; break-inside: avoid; text-align: left; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(223, 226, 229); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">from parameterized import parameterized

class TestCalculateFunc(unittest.TestCase):
'''test func.py'''

num = 1

@parameterized.expand([
["eric", "123456", True], # 可以是list,也可以是元组
["", "123456", True],
["eric", "12345", True],
["lingling", "123456"]
])</pre>

测试用例模块

<pre spellcheck="false" class="md-fences md-end-block contain-cm modeLoaded" lang="python" contenteditable="false" cid="n79" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Consolas, "Liberation Mono", Courier, monospace; font-size: 0.9em; white-space: normal; display: block; break-inside: avoid; text-align: left; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(223, 226, 229); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">def test_login(self, username, password, flag):

这里的参数对应上述列表里的元素,运行时会遍历上述二维列表

''' 登录 '''
res = login(username,password)
self.assertEqual(res,flag)</pre>

setUp() , tearDown()

setUp() 和tearDown()两个方法(本质上是重写了TestCase的这两个方法),

这两个方法在每个测试方法执行前以及执行后执行一次,setUp用来为测试准备环境,tearDown用来清理环境,为之后的测试做准备。

代码实例:

<pre spellcheck="false" class="md-fences md-end-block contain-cm modeLoaded" lang="python" contenteditable="false" cid="n89" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Consolas, "Liberation Mono", Courier, monospace; font-size: 0.9em; white-space: normal; display: block; break-inside: avoid; text-align: left; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(223, 226, 229); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">def setUp(self):
print("do something before test.Prepare environment")

def tearDown(self):
print("do something after test.Clean up")</pre>

这两个方法要写在测试用例里面。如果想要在每个case执行时只各自执行一次,为了达到这样的效果,我们可以把这两个方法设置为类方法。

setUpClass() 和 tearDownClass()再给它们加上类修饰符修饰。

代码实例:

<pre spellcheck="false" class="md-fences md-end-block contain-cm modeLoaded" lang="python" contenteditable="false" cid="n96" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Consolas, "Liberation Mono", Courier, monospace; font-size: 0.9em; white-space: normal; display: block; break-inside: avoid; text-align: left; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(223, 226, 229); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class TestCalculateFunc(unittest.TestCase):
@classmethod
def setUpClass(cls): # 注意,此时为setUpClass(), 不是setUp()
print("do something before test.Prepare environment")

@classmethod
def tearDownClass(cls):
print("do something after test.Clean up")
"""Test calculate_func.py"""</pre>

跳过case

  1. skip装饰器

    <pre spellcheck="false" class="md-fences md-end-block contain-cm modeLoaded" lang="python" contenteditable="false" cid="n104" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Consolas, "Liberation Mono", Courier, monospace; font-size: 0.9em; white-space: normal; display: block; break-inside: avoid; text-align: left; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(223, 226, 229); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">@unittest.skip("I don't want to run this case")
    def test_divides(self):
    ···</pre>

    skip装饰器一共有三个:

    1. unittest.skip(reason)

    2. unittest.skiplf(condition,reason)

    3. unittest.skipUnless(condition,reason)

    skip无条件跳过

    skipif是当condition为True时跳过(只有当条件满足时才跳过)

    skipUnless是当condition为False时跳过(只有当条件满足时才不跳过)

HTMLTestRunner ——输出漂亮的报告

通过下载报告库即可使用

HTMLTestRunner是一个第三方的unittest HTML报告库,首先我们下载HTMLTestRunner.py,并放到当前目录下,或者你的’C:\Python27\Lib’下,就可以导入运行了。

下载地址:

官方原版

修改版

[Python3.x版本

总结

上一篇下一篇

猜你喜欢

热点阅读