今天在写自动化测试用例设计的时候,遇到一个小坑,贴出部分代码如下:
class order_MT(unittest.TestCase):
def setUp(self):
self.driver = login()
To_manage_order(self.driver)
def test_search_order(self):
search_order_test(self.driver)
def test_new_order(self):
add_order_msg(self.driver)
def test_Order_num(self):
Order_num_Verification(self.driver)
def teardown(self):
self.quit()
发现所有用例结束执行结束只调用了一次teardown。 然后仔细查看发现是d要大写。
既然说到这了就扯一扯其他问题:
用例的执行顺序并不是按照先后顺序的,做个小实验:
import unittest
class Test_setup_and_teardwon(unittest.TestCase):
def setUp(self):
print('set up')
def tearDown(self):
print('tear down')
def test_case_B(self):
print('run B')
def test_case_A(self):
print('run A')
def test_case_C(self):
print('run C')
if __name__ == '__main__':
unittest.main()
运行结果:
set up
.run A
tear down
set up
run B
tear down
.set up
run C
tear down
——————————————————————————————————
Ran 3 tests in 0.000s
OK
[Finished in 0.2s]
在unittest中用例执行的先后顺序是根据数字、字母的先后顺序来判定的。那么有什么方法可以让它按照预定的顺序执行呢?
1.用字母、数字先后顺序排序。(这个有比较明显的缺点:用例名称会不够美观、杂乱无章)
2.用testsuite控制用例加载顺序。(缺点是,当case较多 时,逐个添加非常麻烦)
第二种办法的实例:
import unittest
class Test_setup_and_teardwon(unittest.TestCase):
def setUp(self):
print('set up')
def tearDown(self):
print('tear down')
def test_case_B(self):
print('run B')
def test_case_A(self):
print('run A')
def test_case_C(self):
print('run C')
if __name__ == '__main__':
suite = unittest.TestSuite()
suite.addTest(Test_setup_and_teardwon("test_case_A"))
suite.addTest(Test_setup_and_teardwon("test_case_B"))
suite.addTest(Test_setup_and_teardwon("test_case_C"))
runner = unittest.TextTestRunner()
runner.run(suite)
执行结果如下:
set up
.run A
tear down
set up
run B
tear down
set up
run C
tear down
——————————————————————
Ran 3 tests in 0.000s
OK
[Finished in 0.2s]
可见两种方法都是可行的。
但是随之而来的问题就是:
当下一个用例的执行需要依赖上一个用例的执行结果时,应该怎么办?
先来验证一次每个用例是否在结尾处都会执行一次tearDown:
import unittest
class Test_setup_and_teardwon(unittest.TestCase):
def setUp(self):
self.a = 2
self.b = 3
print('set up over :')
print('a = %s, b = %s \n'%(self.a, self.b))
def tearDown(self):
a = 0
b = 0
print('tear down over :')
print('a = %s, b = %s'%(a, b))
print('__________________________________________________________')
def test_case_B(self):
a = self.a*2
b = self.b*2
print('run B over :')
print('a= %s, b = %s \n'%(a, b))
def test_case_A(self):
a = self.a*2
b = self.b*2
print('run A over :')
print('a = %s, a = %s \n'%(a, b))
if __name__ == '__main__':
suite = unittest.TestSuite()
suite.addTest(Test_setup_and_teardwon("test_case_A"))
suite.addTest(Test_setup_and_teardwon("test_case_B"))
runner = unittest.TextTestRunner()
runner.run(suite)
运行结果:
set up over :
a = 2, b = 3
run A over :
a = 4, a = 6
tear down over :
a = 0, b = 0
__________________________________________________________
set up over :
.a = 2, b = 3
run B over :
a= 4, b = 6
tear down over :
a = 0, b = 0
__________________________________________________________
.
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
[Finished in 0.2s]
可见确实每个case运行结束之后都会调用tearDown,那么如何避免呢?
通常我会使用两个方法:
1.在需要依赖的case中,将上一个case单独拎出来做一个公用的方法。然后在进入这个case时先调用该方法。例如caseB需要caseA返回的结果,那么我先将caseA封装成一个通用的方法,根据传入的参数不同返回不同的值。然后在caseB中获取返回的值,作为caseB开始前的另一种变相的 ‘setup’。
修改代码如下:
import unittest
def set_A(a, b):
a = a*2
b = b*2
return (a, b)
class Test_setup_and_teardwon(unittest.TestCase):
def setUp(self):
self.a = 2
self.b = 3
print('set up over :')
print('a = %s, b = %s \n'%(self.a, self.b))
def tearDown(self):
a = 0
b = 0
print('tear down over :')
print('a = %s, b = %s'%(a, b))
print('__________________________________________________________')
def test_case_B(self):
(a, b) = set_A(self.a, self.b)
a = a*2
b = b*2
print('run B over :')
print('a= %s, b = %s \n'%(a, b))
def test_case_A(self):
a = self.a*2
b = self.b*2
print('run A over :')
print('a = %s, a = %s \n'%(a, b))
def test_case_C(self):
a = self.a*4
b = self.b*4
print('run C over :')
print('a = %s, a = %s \n'%(a, b))
if __name__ == '__main__':
suite = unittest.TestSuite()
suite.addTest(Test_setup_and_teardwon("test_case_A"))
suite.addTest(Test_setup_and_teardwon("test_case_B"))
suite.addTest(Test_setup_and_teardwon("test_case_C"))
runner = unittest.TextTestRunner()
runner.run(suite)
结果:
set up over :
.a = 2, b = 3
run A over :
a = 4, a = 6
tear down over :
a = 0, b = 0
__________________________________________________________
set up over :
a = 2, b = 3
run B over :
a= 8, b = 12
tear down over :
.a = 0, b = 0
__________________________________________________________
set up over :
a = 2, b = 3
run C over :
.a = 8, a = 12
tear down over :
a = 0, b = 0
__________________________________________________________
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
[Finished in 0.1s]
可见如此做确实可以保证保持用例依赖性的同时,也保证其他用例的测试环境的“干净”。
但是在测试用例难以解耦的情况下,每写下一个用例就要封装上一个用例的方法,这会导致代码长度翻倍增长。
因此在此处对于用例之间依赖性较强的模块,控制setUp和tearDown方法只执行一次就很有必要。
2.使用修饰器控制setUp和tearDown的执行条件。
代码如下:
import unittest
def set_A(a, b):
a = a*2
b = b*2
return (a, b)
class Test_setup_and_teardwon(unittest.TestCase):
@classmethod
def setUpClass(self):
self.a = 2
self.b = 3
print('set up over :')
print('a = %s, b = %s \n'%(self.a, self.b))
@classmethod
def tearDownClass(self):
a = 0
b = 0
print('tear down over :')
print('a = %s, b = %s'%(a, b))
print('__________________________________________________________')
def test_case_B(self):
(a, b) = set_A(self.a, self.b)
a = a*2
b = b*2
print('run B over :')
print('a= %s, b = %s \n'%(a, b))
def test_case_A(self):
a = self.a*2
b = self.b*2
print('run A over :')
print('a = %s, a = %s \n'%(a, b))
def test_case_C(self):
a = self.a*4
b = self.b*4
print('run C over :')
print('a = %s, a = %s \n'%(a, b))
if __name__ == '__main__':
suite = unittest.TestSuite()
suite.addTest(Test_setup_and_teardwon("test_case_A"))
suite.addTest(Test_setup_and_teardwon("test_case_B"))
suite.addTest(Test_setup_and_teardwon("test_case_C"))
runner = unittest.TextTestRunner()
runner.run(suite)
执行结果:
set up over :
a = 2, b = 3
run A over :
a = 4, a = 6
..run B over :
a= 8, b = 12
run C over :
a = 8, a = 12
tear down over :
a = 0, b = 0
__________________________________________________________
.
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
[Finished in 0.2s]
因此,将不同模块对于用例之间的解耦很有必要,这样可以给不同用例提供整洁的测试环境。
耦合度较高的用例可以配合使用上述几种方法设计测试环境。