1 测试金字塔
图 1软件测试金字塔
图 1是Main Cohn提出的软件测试金字塔,他认为作为一个测试工程师应该把大量的工作花在单元测试和接口测试,而其余的发在UI测试以及探索式测试。纵然,单元测试的优点很突出,它接近于代码本身,运行速度快,开发可以一边写产品代码一边写单元测试代码,一旦在单元测试中发现缺陷,可以马上找到对应的产品代码来进行修改。然而单元测试的缺点也很明显,就是你有多少产品代码,就要有相应的单元测试代码与它相对应,这样造成的结果是单元测试代码等于甚至超过与产品代码的数量,这也就是为什么单元测试在一般的中小型企业很难全面推广的原因。对于基于UI层面的测试由于需求变更,页面调整比较频繁,所以在许多企业,基于UI的自动化测试仅仅用于需求不带变化的核心功能的自动化,往往是一些冒烟测试用例。而基于两者之间的接口测试(Interface Test),基于代码量不是很多,变更比较少的优势下越来越得到各大企业的支持。
2 unittest
由于本文是介绍Django的,而Django是基于Python语言的,所以我们接下来介绍在这里我主要介绍基于Python Requests的软件接口测试。首先让我们来了解一下基于Python的unittest,unittest 原名为pytest,他是属于XUnit框架下的。先让我们来看一下一段产品代码。
Calculator.py
#!/usr/bin/env python #coding:utf-8 class calculator: def __init__(self, a, b): self.a=int(a) self.b=int(b) def myadd(self): return self.a+self.b def mysubs(self): return self.a-self.b def mymultiply(self): return self.a*self.b def mydivide(self): try: return self.a/self.b except ZeroDivisionError: print ("除数不能为零") return 9999999999999999
很显然这个代码实现的是加、减、乘、除四则运算的功能。类calculator有两个成员变量,self.a和self.b,myadd、mysubs、mymultiply、mydivide分别实现self.a+self.b、self.a-self.b、self.a*self.b、self.a/self.b四个功能,在mydivide中,如果被除数self.b为0,我们就进行对应的处理,打印"除数不能为零"的警告,然后返回一个很大的数:
9999999999999999。现在让我们来看一看这段代码所对应的unittest框架的测试代码。
CalculatorTest.py
#!/usr/bin/env python #coding:utf-8 import unittest from Calculator import calculator class calculatortest(unittest.TestCase): def setUp(self): print ("Test start!") def test_base(self): j=calculator(4,2) self.assertEqual(j.myadd(),6) self.assertEqual(j.mysubs(),2) self.assertEqual(j.mymultiply(),8) self.assertEqual(j.mydivide(),2) def test_divide(self): j=calculator(4,0) self.assertEqual(j.mydivide(),9999999999999999) def tearDown(self): print ("Test end!") if __name__=='__main__': #构造测试集 suite=unittest.TestSuite() suite.addTest(calculatortest("test_base")) suite.addTest(calculatortest("test_divide")) #运行测试集合 runner=unittest.TextTestRunner() runner.run(suite)
- 首先我们使用unittest测试框架必须先importunittest类,unittest类是Python自带的测试类,只要你安装了Python,这个类就自动安装上了。
- 然后我们引入被测试类:fromCalculator import calculator。
- unittest的测试类参数必须为unittest.TestCase。
- 和其他XUnit测试框架一样,unittest也存在着一个初始化函数和清除函数,分别定义为def setUp(self):和def tearDown(self):,由于在这里没有具体实际性的操作我们仅仅在def setUp(self):函数中打印一个"Test start!"字符串;在def tearDown(self):函数中打印一个"Testend!"字符串。
- unittest具体测试函数的函数名必须以test_开头,这个有点类似于JUnit3,j=calculator(4,2)先定义一个self.a =4和self.b = 2的类变量j,然后通过断言self.assertEqual()函数来验证是不是计算结果与预期结果一致。
- 在deftest_divide(self):函数中我们专门对被除数为0的情况进行了测试。
unittest的主函数为与其他主函数一样为if__name__=='__main__':,先通过suite=unittest.TestSuite()来构造测试集,然后通过suite.addTest(calculatortest("test_base")),suite.addTest(calculatortest("test_divide"))把两个测试函数加进去,接下来通过runner=unittest.TextTestRunner(),runner.run(suite)来执行测试工作。
当许多测试文件需要批量运行的时候,我们可以进行如下操作:
1, 把这些测试文件的文件名定义成一个可以用正则函数匹配的模式,比如都以Test开始或结尾的.py文件。
2, 建立一个批处理py文件,比如runtest.py。
runtest.py
#!/usr/bin/env python #coding:utf-8 import unittest test_dir='./' discover=unittest.defaultTestLoader.discover(test_dir,pattern="*Test.py") if __name__=='__main__': runner=unittest.TextTestRunner() runner.run(discover)
test_dir:定义测试文件的路径,这里为当前路径。
discover=unittest.defaultTestLoader.discover(test_dir,pattern="*Test.py")为调用测试路径下以Test结尾的.py文件(pattern="*Test.py")
然后在主函数中通过调用runner=unittest.TextTestRunner(),runner.run(discover)两行代码来实现匹配的所有文件中的测试用例的执行。
既然介绍到了unittest的批量操作,在这里我很有必要来介绍一下如何通过unittest来生成一封好看的测试报告。
我们先到网站http://tungwaiyip.info/software/HTMLTestRunner.html下载HTMLTestRunner.py文件放入到%PYTHON_HOME%\Lib\目录下。如果你使用的是
Python2.X就不需要进行修改,否则请作如下修改:
94行 import StringIO 改为 import io 539行 self.outputBuffer = StringIO.StringIO() 改为 self.outputBuffer = io.StringIO() 631行 print >>sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime) 改为 print (sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime)) 642行 if not rmap.has_key(cls): 改为 if not cls in rmap: 766行 uo = o.decode('latin-1') 改为 uo = o 772行 ue = e.decode('latin-1')改为 ue = e
这样我们在runtest.py头部加入fromHTMLTestRunner import HTMLTestRunner,runner.run(discover)前面加上fp=open("result.html","wb"),runner=HTMLTestRunner(stream=fp,title='测试报告',description='测试用例执行报告'),后面加上fp.close(),运行测试用例完毕就可以生成一份美观的基于HTML的测试报告了,最后的runtest.py代码如下。
runtest.py
#!/usr/bin/env python #coding:utf-8 import unittest from HTMLTestRunner import HTMLTestRunner test_dir='./' discover=unittest.defaultTestLoader.discover(test_dir,pattern="*Test.py") if __name__=='__main__': runner=unittest.TextTestRunner() #以下用于生成测试报告 fp=open("result.html","wb") runner =HTMLTestRunner(stream=fp,title='测试报告',description='测试用例执行报告') runner.run(discover) fp.close()
图2测试报表,当然这里的测试用例刚才介绍的要多。
图2 unittest测试报表