软件测试

[Py_18]Python单元测试(UnitTest)用例的管理

2018-07-16  本文已影响33人  Fighting_001

目录结构

一、新增测试用例管理
二、用例公共部分的合并
三、用例执行的顺序
四、用例综合框架的管理
五、跳过测试和设置预期失败

一、新增测试用例管理

在前文UnitTest单元测试概述&案例实践是针对单个add方法进行单元测试,本文则是针对需要多个方法进行单元测试的场景。

实践案例-流程:
1)在A文件中创建一个Math类,在Math类中定义2个方法(add和sub方法)
2)在B文件中导入A文件中的所有方法,然后分别创建2个测试类(Test_add和Test_sub),并在这两个类中定义各自的测试方法
3)构造测试集,分别将2个测试类中测试方法加载到测试集中
4)执行单元测试用例

编写代码:

class Math:
    def __init__(self,a,b): # 初始化
        self.a = int(a)
        self.b = int(b)

    def add(self):
        return self.a+self.b

    def sub(self):
        return self.a-self.b
from calculator2 import Math
import unittest

class Test_add(unittest.TestCase):
    def setUp(self):
        print("--Starting Test--")

    def test_add1(self):
        j = Math(5,5)
        self.assertEqual(j.add(),10)

    def test_add2(self):
        j = Math(10,20)
        self.assertEqual(j.add(),30)

    def tearDown(self):
        print("--End--")

class Test_sub(unittest.TestCase):
    def setUp(self):
        print("--Starting Test--")

    def test_sub1(self):
        i = Math(8,8)
        self.assertEqual(i.sub(),0)

    def test_sub2(self):
        i = Math(5,3)
        self.assertEqual(i.sub(),2)

    def tearDown(self):
        print("--End--")

if __name__=='__main__':
    suite = unittest.TestSuite()
    suite.addTest(Test_add("test_add1"))
    suite.addTest(Test_add("test_add2"))
    suite.addTest(Test_sub("test_sub1"))
    suite.addTest(Test_sub("test_sub2"))

    runner = unittest.TextTestRunner()
    runner.run(suite)

执行结果:
Ran 4 tests in 0.016s

OK
--Starting Test--
--End--
--Starting Test--
--End--
--Starting Test--
--End--
--Starting Test--
--End--

二、用例公共部分的合并

以上代码中的每个测试类都有setUp()和tearDown()方法,且两个方法都是一样的,用于打印测试开始和结束的提示语.可以考虑将其合并在一个类中,作为每个测试类共同的父类来继承,就不需要每次单独去写。

from calculator2 import Math
import unittest

class Test_StartEnd(unittest.TestCase):
    def setUp(self):
        print("--Test Start--")

    def tearDown(self):
        print("--Test End--")

class Test_add(Test_StartEnd):
    def test_add(self):
        j = Math(5,5)
        self.assertEqual(j.add(),10)

class Test_sub(Test_StartEnd):
    def test_sub(self):
        i = Math(3,2)
        self.assertEqual(i.sub(),1)

# 执行所有的2个测试用例
if __name__=='__main__':
    unittest.main()

执行结果:
Ran 2 tests in 0.016s

OK
--Test Start--
--Test End--
--Test Start--
--Test End--

三、用例执行的顺序

以下代码,在不作代码调控设置的前提下,执行过程是否是与常规认为的自上而下顺序执行呢?(C-->B-->A-->D ?)

import unittest

class Test_StartEnd(unittest.TestCase):
    def setUp(self):
        print("--Test Start--")

    def tearDown(self):
        print("--Test End--")

class Test2(Test_StartEnd):
    def test_c(self):
        print("C")

    def test_b(self):
        print("B")

class Test1(Test_StartEnd):
    def test_a(self):
        print("A")

class Test3(Test_StartEnd):
    def test_d(self):
        print("D")

实际执行结果:
--Test Start--
A
--Test End--
--Test Start--
B
--Test End--
--Test Start--
C
--Test End--
--Test Start--
D
--Test End--

从执行结果中,可以分析出:
对于PyCharm默认的runner为Unittests的情况
1)代码执行顺序的优先级:测试类>>测试方法。即先根据测试类排序区分,然后再分别定位到每个测试类的不同测试方法的排序
2)测试类or测试方法:根据字母or数字从小到大排序执行,如1-->2-->3、a-->b-->c-->d

现在通过代码中调控按照设置的顺序来执行测试用例:

import unittest

class Test_StartEnd(unittest.TestCase):
    def setUp(self):
        print("--Test Start--")

    def tearDown(self):
        print("--Test End--")

class Test2(Test_StartEnd):
    def test_c(self):
        print("C")

    def test_b(self):
        print("B")

class Test1(Test_StartEnd):
    def test_a(self):
        print("A")

class Test3(Test_StartEnd):
    def test_d(self):
        print("D")

if __name__=='__main__':
    suite = unittest.TestSuite()
    suite.addTest(Test3("test_d"))
    suite.addTest(Test2("test_c"))
    suite.addTest(Test1("test_a"))
    suite.addTest(Test2("test_b"))

    runner = unittest.TextTestRunner()
    runner.run(suite)

执行结果:

  1. 若按PyCharm默认的runner为Unittests执行,则可能依然还是没有按照设定的顺序执行
  2. 若修改PyCharm的默认的runner为py.test执行,则PyCharm中可按照测试类自上而下执行用例(具体修改方法,可参看设置PyCharm根据测试类自上而下顺序执行代码
默认runner为py.test
  1. 若以cmd命令行执行,则执行效果可按照程序设定控制的顺序执行输出
cmd命令方式操作

四、用例综合框架的管理

以上测试用例及用例执行都是卸载一个文件中,当用例数量不断增加,用例的执行与管理会变得非常冗杂和低效率,因此需要对用例根据具体的功能模块来使用单独的模块进行管理。

案例:新建一个文件目录Test_Project,其下包含4个python文件进行用例的管理和执行分工
① StartEnd.py ==>setUp与tearDown的管理
② calculator.py ==>加减法运算方法的实现
③ test_add.py ==>加法测试模块
④ test_sub.py ==>减法测试模块
⑤ runtest.py ==>用例执行管理模块


StartEnd.py

import unittest

class setUp_tearDown(unittest.TestCase):
    def setUp(self):
        print("--Start Test--")

    def tearDown(self):
        print("--Test End--")

calculator.py

class Math:
    def __init__(self,a,b):
        self.a = int(a)
        self.b = int(b)

    def add(self):
        return self.a+self.b

    def sub(self):
        return self.a-self.b

test_add.py

from calculator import *
from StartEnd import *

class Test_Add(setUp_tearDown):
    def test_add1(self):
        i = Math(5,5)
        self.assertEqual(i.add(),10)

    def test_add2(self):
        i = Math(1,2)
        self.assertEqual(i.add(),3)

test_sub.py

from calculator import *
from StartEnd import *

class Test_Sub(setUp_tearDown):
    def test_sub1(self):
        j = Math(6,6)
        self.assertEqual(j.sub(),0)

    def test_sub2(self):
        j = Math(7,6)
        self.assertEqual(j.sub(),1)

runtest.py

import unittest

# 被测脚本的路径
test_dir = './' # 此时表示当前目录
# 使用discover可以一次调用多个脚本;pattern设置脚本名称的匹配规则
discover = unittest.defaultTestLoader.discover(test_dir,pattern='test*.py')

if __name__ == '__main__':
    runner = unittest.TextTestRunner()
    runner.run(discover)

在以上第⑤个python文件runtest.py执行代码,调用其他模块,执行的结果为:



五、跳过测试和设置预期失败

① @unittest.skip("reason") ==>直接跳过测试
② @unittest.skipIf(condition,"reason") ==>条件为真,跳过测试
③ @unittest.skipUnless(condition,"reason") ==>条件为假,跳过测试
④ @unittest.expectedFailure ==>设置预期为失败


@unittest.skip("reason")

import unittest

class Test_StartEnd(unittest.TestCase):
    def setUp(self):
        print("--Test Start--")

    def tearDown(self):
        print("--Test End--")

@unittest.skip("skip Test2")
class Test2(Test_StartEnd):
    def test_c(self):
        print("C")

    def test_b(self):
        print("B")

class Test1(Test_StartEnd):
    def test_a(self):
        print("A")

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

执行结果:

直接跳过测试类

@unittest.skipIf(condition,"reason")

import unittest

class Test_StartEnd(unittest.TestCase):
    def setUp(self):
        print("--Test Start--")

    def tearDown(self):
        print("--Test End--")

class Test2(Test_StartEnd):
    def test_c(self):
        print("C")

    @unittest.skipIf(1,"skip test_b")
    def test_b(self):
        print("B")

class Test1(Test_StartEnd):
    def test_a(self):
        print("A")

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

执行结果:

判断条件为真,跳过测试方法

@unittest.skipUnless(condition,"reason")

import unittest

class Test_StartEnd(unittest.TestCase):
    def setUp(self):
        print("--Test Start--")

    def tearDown(self):
        print("--Test End--")

class Test2(Test_StartEnd):
    @unittest.skipUnless(1>2,"skip test_c")
    def test_c(self):
        print("C")

    def test_b(self):
        print("B")

class Test1(Test_StartEnd):
    def test_a(self):
        print("A")

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

执行结果:

判断条件为假,跳过测试方法

@unittest.expectedFailure

import unittest

class Test_StartEnd(unittest.TestCase):
    def setUp(self):
        print("--Test Start--")

    def tearDown(self):
        print("--Test End--")

class Test2(Test_StartEnd):
    def test_c(self):
        print("C")

    def test_b(self):
        print("B")

class Test1(Test_StartEnd):
    @unittest.expectedFailure
    def test_a(self):
        print("A")

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

执行结果:

设置预期失败

【附】
⑤ @classmethod ==>实现对测试类运行前&后的处理

import unittest

class Test_StartEnd(unittest.TestCase):
    def setUp(self):    # 实现对每个测试方法执行前的处理
        print("--Test Start--")

    def tearDown(self): # 实现对每个测试方法执行后的处理
        print("--Test End--")

class Test2(Test_StartEnd):
    def test_c(self):
        print("C")

    def test_b(self):
        print("B")

class Test1(Test_StartEnd):
    @classmethod
    def setUpClass(cls):    # 实现对测试类Test1运行前的处理
        print("Class module start test>>>>>>>>>")

    @classmethod
    def tearDownClass(cls): # 实现对测试Test1运行后的处理
        print("Class module test end>>>>>>>>>>>")

    def test_a(self):
        print("A")

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

执行结果:

测试类、测试方法-前&后的处理对比
上一篇下一篇

猜你喜欢

热点阅读