人们针对一个具体问题,经过分析和设计,最后用编程语言写出了一个程序,若是它经过了语言解释器(编译器)的检查,能够运行了,那么下一步的工做就是设法确认它确实知足了咱们需求。这篇文章就是讨论怎么确认程序是否知足用户提出的需求。php
知足需求,换言之就是功能正常,确认功能正常能够从如下几个方面确认:html
量化后的作法就是经过一系列的试运行,检查程序的行为、输入和输出,若是检查中发现了问题,就纠正、改进。这个也是功能测试和安全测试的初衷。java
测试考虑的基本问题就是怎么运行程序,须要提供什么数据,才能最大限度的检查程序的各类行为和状况,最大可能的挖出程序中的错误和缺陷。基于设计什么测试流程、提供什么参数这种检查程序运行的一套数据被称为一个测试用例。一个测试用例就是可量化的测试流程。python
确认测试用例又区分两类方式:编程
黑盒测试
就是不看代码,直接上手程序的使用测试。这里不讨论黑盒安全
白盒测试
白盒测试的基础是看程序的内部结构(代码)和可能产生的执行路径,根据内部结构来选择测试的用例,使程序在试验性运行中就能表示出尽量多的不一样行为。这个作法的基本理念就是:若是全部可能执行的路径(顺序、条件、while、for、嵌套...执行结构)都能给出正确的结果,那么程序的正确性就能获得保证。app
各种的语言都会提供单元测试的库,Python也不例外,python通常使用PyUnit(unittest)库,unittest是Python自带的单元测试框架,用于编写和运行可重复的测试,下面介绍怎么用unittest来测试函数的用法,我这里只是简单用了几个测试方法,更多测试方法请查阅官网(https://docs.python.org/3/library/unittest.html)。框架
3个须要测试的函数:编程语言
def mysum(a, b): return a + b def mysubtraction(a, b): return a - b def is_evenNumbers(x): if (x % 2) == 0: return True else: return False
测试函数的方法:函数
import unittest import testbox.mymath as mymath class Test(unittest.TestCase): def setUp(self): print("The unit test function start.") def test_mysum(self): self.assertEqual(mymath.mysum(1, 2), 3, "mysum function have error!") def test_mysubtraction(self): self.assertEqual(mymath.mysubtraction(2, 1), 1, "mysubtraction function have error!") def test_is_evenNumbers(self): self.assertTrue(mymath.is_evenNumbers(2), "error") def test_is_evenNumbers2(self): self.assertFalse(mymath.is_evenNumbers(3), "error") def tearDown(self): print("The unit test end.") if __name__ == '__main__': unittest.main()
输出:
Testing started at 12:26 PM ... The unit test function start. The unit test end. The unit test function start. The unit test end. The unit test function start. The unit test end. The unit test function start. The unit test end.
功能其实和上面测试函数用法是同样的,只不过assert能够直接使用在代码里。这个关键字也比较生僻,也没见什么场景须要用它,也就这里为了作个案例,我才用它写了个demo。
def testasserts(a): assert a == 2, Exception("parameter a not is 2, so have a error.") if a == 2: print("function run.") print("OK. function end.") if __name__ == '__main__': testasserts(1) print("Program is end.")
输出:
Traceback (most recent call last): File "/Users/Mysticbinary/Document/code/personage/python/TestPython1/testbox/testadd.py", line 9, in <module> testasserts(1) File "/Users/Mysticbinary/Document/code/personage/python/TestPython1/testbox/testadd.py", line 2, in testasserts assert a == 2, Exception("parameter a not is 2, so have a error.") AssertionError: parameter a not is 2, so have a error.
类功能的测试和函数测试同样,只不过有一个窍门就是,测试、使用类的时候都须要先实例化类,而实例化类的操做,均可以放在setUp()里面操做。
须要测试的类:
class Library: allBook = ["php", "java"] def __init__(self): print("Library class create completion.") def savebook(self, bookname): self.allBook.append(bookname); return self.allBook def showbook(self): print(self.allBook) return self.allBook
测试类的方法:
import unittest import testbox.myclass as myc class TestClass(unittest.TestCase): def setUp(self): print("The unit test class start.") self.library = myc.Library() self.newbook = "python" def test_savebook(self): self.assertIn(self.newbook, self.library.savebook(self.newbook), "errow 1") def test_showbook(self): self.assertIn(self.newbook, self.library.showbook(), "errow 2") def tearDown(self): print("The unit test end.") if __name__ == '__main__': unittest.main()
输出:
Testing started at 12:31 PM ... The unit test class start. Library class create completion. The unit test end. The unit test class start. Library class create completion. ['php', 'java', 'python'] The unit test end.
我前面说过,功能测试和安全测试都有一样的初衷,可是具体的测试手法二者不太同样,可是一些特定的场景下,使用单元测试的方法,也是能测试一些安全问题的,好比说测接口越权、短信接口重复致使的短信轰炸问题等。我这里只是抛砖引玉一下经过单元测试的手法来作安全测试的例子,但为作深刻研究。
def send_message(phone): keys = phones_dict.keys() if phone not in keys: phones_dict[phone] = 0 else: if phones_dict[phone] < 10: # 执行发短信的流程 phones_dict[phone] = (phones_dict[phone] + 1) print("已经发送了{}次短信".format(phones_dict[phone])) return "success" else: print("抱歉,该{}手机号 已经达到今天发短信的上限,请明天再来。".format(phone)) return "error"
测试发短信函数安全的测试用例:
def test_send_message(self): result = list() for i in range(0, 11): result.append(sms.send_message("13193388105")) print(result) self.assertNotIn("error", result, "send_message have error.")
输出:
Testing started at 9:48 PM ... The unit test function start. 已经发送了1次短信 已经发送了2次短信 已经发送了3次短信 已经发送了4次短信 已经发送了5次短信 已经发送了6次短信 已经发送了7次短信 已经发送了8次短信 已经发送了9次短信 已经发送了10次短信 [None, 'success', 'success', 'success', 'success', 'success', 'success', 'success', 'success', 'success', 'success'] The unit test end.
除了单元测试,设置模块的运行入口(main)也是一种测试方式,就是针对每一个模块单独的调用里面的函数。__main__实际上是一个全局变量,解释器发现若是该模块是被导入的,那么__main__就会被赋值为这个模块的名字,若是这个模块是做为主模块启动时,那么解释器就会给__main__赋值为“main”字符串。