单元测试框架的作用
-
发现测试用例
-
执行测试用例
-
判断测试结果
-
生成测试报告
1.unittest简介
Unittest是Python内部自带的一个单元测试的模块,它设计的灵感来源于Junit,具有和Junit类似的结构,有过Junit经验的朋友可以很快上手。Unittest具备完整的测试结构,支持自动化测试的执行,对测试用例集进行组织,并且提供了丰富的断言方法,最后生成测试报告。Unittest框架的初衷是用于单元测试,但也不限于此,在实际工作中,由于它强大的功能,提供的完整的测试流程,我们往往将其用于自动化测试的各个方面,例如在本书中大量的接口测试实例都会用到Unittest。
unitest单元测试框架提供了创建测试用例、测试套件和批量执行测试用例。
2.测试固件
# unittest单元测试框架提供了名为setUp和tearDown的测试固件。
他的执行顺序是先执行setUp方法,再执行具体的测试用例test_baidu_so,最后执行tearDown方法。
1.测试固件每次均执行
# 1.测试固件每次均执行
import unittest
class BaiduTest(unittest.TestCase):
def setUp(self):
print('start')
def tearDown(self):
print('end')
def test_baidu_so(self):
print("测试用例执行")
if __name__ == '__main__':
unittest.main(verbosity=2)
实战百度首页,两个测试case:
# 他的执行顺序是先执行setUp方法,再执行具体的测试用例test_baidu_so,最后执行tearDown方法。
import unittest
import time as t
from selenium.webdriver.common.by import By
from selenium import webdriver
class BaiduTest(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.implicitly_wait(30)
url = 'http://www.baidu.com'
self.driver.get(url)
print('start')
def tearDown(self):
self.driver.quit()
print('end')
def test_baidu_new(self):
self.driver.find_element(By.LINK_TEXT, '新闻').click()
print("测试用例执行")
def test_baidu_map(self):
self.driver.find_element(By.LINK_TEXT, '地图').click()
print("测试用例执行")
if __name__ == '__main__':
unittest.main(verbosity=2)
2.测试固件只执行一次
unittest单元测试框架中使用setUpClass和tearDownClass方法,可以只打开一次浏览器。
该固件方法是类方法,需要在方法上面加装饰器@classmethod
# 他的执行顺序是先执行setUp方法,再执行具体的测试用例test_baidu_so,最后执行tearDown方法。
import unittest
import time as t
from selenium.webdriver.common.by import By
from selenium import webdriver
# 测试固件只执行一次
class BaiduTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver = webdriver.Chrome()
cls.driver.maximize_window()
cls.driver.implicitly_wait(30)
url = 'http://www.baidu.com'
cls.driver.get(url)
print('start')
@classmethod
def tearDownClass(cls):
cls.driver.quit()
print('end')
def test_baidu_new(self):
self.driver.find_element(By.LINK_TEXT, '新闻').click()
print("测试用例执行1")
def test_baidu_map(self):
self.driver.find_element(By.LINK_TEXT, '地图').click()
print("测试用例执行2")
if __name__ == '__main__':
unittest.main(verbosity=2)
类方法中不能使用driver = self.driver,否则只会执行一个case
# 他的执行顺序是先执行setUp方法,再执行具体的测试用例test_baidu_so,最后执行tearDown方法。
import unittest
import time as t
from selenium.webdriver.common.by import By
from selenium import webdriver
# 测试固件只执行一次
class BaiduTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver = webdriver.Chrome()
cls.driver.maximize_window()
cls.driver.implicitly_wait(30)
url = 'http://www.baidu.com'
cls.driver.get(url)
print('start')
@classmethod
def tearDownClass(cls):
cls.driver.quit()
print('end')
def test_baidu_new(self):
#driver = self.driver
self.driver.find_element(By.LINK_TEXT, '新闻').click()
t.sleep(3)
self.driver.get("http://www.baidu.com")
print("当前获取的地址为百度:{0}".format(driver.current_url))
print("测试用例执行1")
def test_baidu_map(self):
#driver = self.driver
self.driver.find_element(By.LINK_TEXT, '地图').click()
t.sleep(3)
self.driver.get("http://www.baidu.com")
t.sleep(3)
print("测试用例执行2")
if __name__ == '__main__':
unittest.main(verbosity=2)
3.测试执行
'''可以看到测试用例的执行是在主函数中,unittest调用的是main,代码如: main = TestProgram。
在unittest模块中包含的main方法,可以方便的将测试模块转变为可以运行的测试脚本。main使用unittest.TestLoader类来自动查找和加载模块内的测试用例,例:
def createTests(self):
if self.testNames is None:
self.test = self.testLoader.loadTestsFromModule(self.module)
else:
self.test = self.testLoader.loadTestsFromNames(self.testNames, self.module)
在执行测试用例时,在main方法中加入了verbosity=2,代码:unittest.main(verbosity=2)
在verbosity中默认是1,0代表执行的测试总数和全局结果,2代表显示详细的信息
# 他的执行顺序是先执行setUp方法,再执行具体的测试用例test_baidu_so,最后执行tearDown方法。
import unittest
import time as t
from selenium.webdriver.common.by import By
from selenium import webdriver
class BaiduTest(unittest.TestCase):
@classmethod
def setUpClass(self):
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.implicitly_wait(30)
url = 'http://www.baidu.com'
self.driver.get(url)
print('start')
@classmethod
def tearDownClass(self):
self.driver.quit()
print('end')
def test_baidu_title(self):
self.assertEqual(self.driver.title, '百度一下,你就知道')
print("测试用例执行1")
def test_baidu_map(self):
self.assertEqual(self.driver.current_url, 'https://www.baidu.com/')
print("测试用例执行2")
if __name__ == '__main__':
unittest.main(verbosity=2)
参考文献链接:
https://www.jianshu.com/p/14d880d81346
unittest官方文档:
unittest --- 单元测试框架 — Python 3.11.2 文档
def __init__(self, module='__main__', defaultTest=None, argv=None,
testRunner=None, testLoader=loader.defaultTestLoader,
exit=True, verbosity=1, failfast=None, catchbreak=None,
buffer=None, warnings=None, *, tb_locals=False):
main = TestProgram,也就是main是TestProgram类的一个实例,当这个时候就会实例化地执行init函数。
main()里有很多参数
module='main' : 要放在if name == 'main' 是入口函数。
defaultTest=None:默认为全部,可以指定运行某一个。
argv: 可以传递参数进去
testRunner: 测试运行器
testLoader: 测试加载器,使用的是默认的测试使用加载器
exit=True: 是否在测试程序完成之后关闭程序。
verbosity=1:显示信息的详细程度
-
<=0 只显示用例 的总数和全局的执行结果
-
1 默认值,显示用例总数和全局结果,并且对每个用例的结果有个标。
.成功
F失败
E错误
S用例跳过 -
>=2 显示用例总数和全局结果,并输出每个用例的详解的结果
failfast=None:是否在测试失败时终止测试
catchbreak=None:
buffer=None:
warnings=None:
tb_locals=False:
4.构建测试套件
unittest模块中的TestSuite类表示
1.按顺序执行:TestSuite类
import time as t
from selenium import webdriver
from selenium.webdriver.common.by import By
import unittest
class BaiduTest(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.driver = webdriver.Chrome()
cls.driver.maximize_window()
cls.driver.implicitly_wait(30)
cls.driver.get("http://www.baidu.com")
@classmethod
def tearDownClass(cls) -> None:
t.sleep(3)
cls.driver.quit()
def test_baidu_news(self):
self.driver.find_element(By.LINK_TEXT, '新闻').click()
url = self.driver.current_url
self.assertEqual(url, "https://www.baidu.com/")
print("case1")
def test_baidu_map(self):
self.driver.find_element(By.LINK_TEXT, '地图').click()
self.driver.get("http://www.baidu.com")
print("case2")
if __name__ == '__main__':
# 实例化TestSuite
suite = unittest.TestSuite()
suite.addTest(BaiduTest('test_baidu_news'))
suite.addTest(BaiduTest('test_baidu_map'))
unittest.TextTestRunner(verbosity=2).run(suite)
注:
1.首先需要实例化TestSuite :suite = unittest.TestSuite(),使之成为一个suite对象
2.调用estSuite 类中的方法 addTest 方法,把测试用例添加到测试套件中,最后执行测试套件,从而执行测试套件中的测试用例
3.按照添加到测试套件的顺序执行,也就是说先添加进去的先执行,后添加进去的后执行。
2.按测试类执行:mackSuite类
import time as t
from selenium import webdriver
from selenium.webdriver.common.by import By
import unittest
class BaiduTest(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.driver = webdriver.Chrome()
cls.driver.maximize_window()
cls.driver.implicitly_wait(30)
cls.driver.get("http://www.baidu.com")
@classmethod
def tearDownClass(cls) -> None:
t.sleep(3)
cls.driver.quit()
def test_baidu_news(self):
self.driver.find_element(By.LINK_TEXT, '新闻').click()
url = self.driver.current_url
self.assertEqual(url, "https://www.baidu.com/")
print("case1")
def test_baidu_map(self):
self.driver.find_element(By.LINK_TEXT, '地图').click()
self.driver.get("http://www.baidu.com")
print("case2")
if __name__ == '__main__':
suite = unittest.TestSuite(unittest.makeSuite(BaiduTest))
unittest.TextTestRunner(verbosity=2).run(suite)
注:
测试套件TestSuite 类中,unittest 模块调用了makeSuite 的方法,makeSuite 方法的参数是testCaseClass,也就是测试类,代码如下:
def makeSuie(testCaseClass, prefix='test', sortUsing=cmp, suiteClass=suite.TestSuite):
return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(testCaseClass)
3.加载测试类: TestLoader 类
import time as t
from selenium import webdriver
from selenium.webdriver.common.by import By
import unittest
class BaiduTest(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.driver = webdriver.Chrome()
cls.driver.maximize_window()
cls.driver.implicitly_wait(30)
cls.driver.get("http://www.baidu.com")
@classmethod
def tearDownClass(cls) -> None:
t.sleep(3)
cls.driver.quit()
def test_baidu_news(self):
self.driver.find_element(By.LINK_TEXT, '新闻').click()
url = self.driver.current_url
self.assertEqual(url, "https://www.baidu.com/")
print("case1")
def test_baidu_map(self):
self.driver.find_element(By.LINK_TEXT, '地图').click()
self.driver.get("http://www.baidu.com")
print("case2")
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(BaiduTest)
unittest.TextTestRunner(verbosity=2).run(suite)
4.按测试模块执行 :TestLoader 类
在python中,一个python文件就是一个模块,一个模块中可以有N个测试类,在一个测试类中可以有N个测试用例。
一、skip装饰器,skip装饰器一共有四个
参考文档:unittest----skip装饰器_@unittest.skip_java2013liu的博客-优快云博客
当测试用例写完后,有些模块有改动时候,会影响到部分用例的执行,这个时候我们希望暂时跳过这些用例。
或者前面某个功能运行失败了,后面的几个用例是依赖于这个功能的用例,如果第一步就失败了,后面的用例也就没必要去执行了,直接跳过就行,节省用例执行时间。
@unittest.skip(reason)
Unconditionally skip the decorated test. reason should describe why the test is being skipped.
翻译:无条件跳过用例,reason是说明原因
@unittest.skipIf(condition, reason)
Skip the decorated test if condition is true.
翻译:condition为true的时候跳过
@unittest.skipUnless(condition, reason)
Skip the decorated test unless condition is true.
翻译:condition为False的时候跳过
@unittest.expectedFailure
Mark the test as an expected failure. If the test fails when run, the test is not counted as a failure.
翻译:断言的时候跳过(暂时不知道有啥用,没看懂,貌似断言失败,也变成用例pass了。)
from selenium import webdriver
import unittest
import time as t
from selenium.webdriver.common.by import By as b
class BaiduTest(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.implicitly_wait(30)
self.driver.get("http://www.baidu.com")
def tearDown(self):
print("BaiduTest:run")
t.sleep(3)
self.driver.quit()
def test_title(self):
self.driver.find_element(b.LINK_TEXT, '百度一下,你就知道')
def test_so(self):
so = self.driver.find_element(b.ID, 'kw')
self.assertTrue(so.is_enabled())
def test_002(self):
self.driver.find_element(b.LINK_TEXT, '新闻').click()
@unittest.skip('do not run')
def test_003(self):
self.driver.find_element(b.LINK_TEXT, '地图').click()
class BaiduMap(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.implicitly_wait(30)
self.driver.get("http://www.baidu.com")
def tearDown(self):
print("BaiduMap:run")
t.sleep(3)
self.driver.quit()
def test_baidu_map(self):
self.driver.find_element(b.LINK_TEXT, '地图').click()
self.driver.get("http://www.baidu.com")
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromModule('test_Loader_demo.py')
unittest.TextTestRunner(verbosity=2).run(suite)
注:
测试类分别是BaiduMap和BaiduTest,模块名称为test_Loader_demo.py,TestLoader类直接调用loadTestsFromModule方法放回给指定模块中包含的所有测试用例套件
5.优化测试套件
@staticmethod是一个Python中的装饰器(decorator),用于标记一个静态方法。
静态方法是一种在类中定义的方法,它与实例无关,因此可以在不创建类实例的情况下调用。与普通方法不同,静态方法没有self参数,因此它不能访问实例属性和方法。
在Python中,使用@staticmethod装饰器可以将一个方法转换为静态方法,即使该方法定义在类中。使用静态方法的主要优点是可以在不创建类实例的情况下调用该方法,从而提高代码的灵活性和可重用性。
下面是一个使用@staticmethod装饰器定义静态方法的示例:
在上述示例中,my_static_method方法被@staticmethod装饰器标记为静态方法。因此,可以通过以下方式直接调用该方法:
在调用静态方法时,不需要创建类实例,直接使用类名即可。静态方法可以通过类或类实例来调用,但不可以通过实例访问静态方法。
# 他的执行顺序是先执行setUp方法,再执行具体的测试用例test_baidu_so,最后执行tearDown方法。
import unittest
import time as t
from selenium.webdriver.common.by import By
from selenium import webdriver
# 测试固件只执行一次
class BaiduTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver = webdriver.Chrome()
cls.driver.maximize_window()
cls.driver.implicitly_wait(30)
url = 'http://www.baidu.com'
cls.driver.get(url)
print('start')
@classmethod
def tearDownClass(cls):
cls.driver.quit()
print('end')
def test_baidu_new(self):
# driver = self.driver
self.driver.find_element(By.LINK_TEXT, '新闻').click()
print("当前获取的地址为新闻:{0}".format(self.driver.current_url))
t.sleep(3)
self.driver.get("http://www.baidu.com")
print("当前获取的地址为百度:{0}".format(self.driver.current_url))
print("测试用例执行1")
def test_baidu_map(self):
# driver = self.driver
self.driver.find_element(By.LINK_TEXT, '地图').click()
t.sleep(3)
print("当前获取的地址为地图:{0}".format(self.driver.current_url))
self.driver.get("http://www.baidu.com")
t.sleep(3)
print("测试用例执行2")
@staticmethod
def suite(self):
suite = unittest.TestLoader().loadTestsFromTestCase(self)
return suite
if __name__ == '__main__':
unittest.TextTestRunner(verbosity=2).run(BaiduTest.suite(BaiduTest))
5.分离测试固件
创建公共模块InitTest_demo.py,类名为InitTest,代码如下:
import unittest
from selenium import webdriver
class InitTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver = webdriver.Chrome()
cls.driver.maximize_window()
cls.driver.implicitly_wait(30)
url = 'http://www.baidu.com'
cls.driver.get(url)
@classmethod
def tearDownClass(cls):
cls.driver.quit()
print('end')
测试类继承了InitTest,继承后,在测试类中直接编写要执行的测试用例,代码如下:
import unittest
from InitTest_demo import InitTest
from selenium.webdriver.common.by import By as b
class BaiduTest(InitTest):
def test_baidu_news(self):
self.driver.find_element(b.LINK_TEXT, '新闻').click()
url = self.driver.current_url
self.assertEqual(url, "https://www.baidu.com/")
print("case1")
def test_baidu_map(self):
self.driver.find_element(b.LINK_TEXT, '地图').click()
self.driver.get("http://www.baidu.com")
print("case2")
if __name__ == '__main__':
unittest.main(verbosity=2)
''' suite = unittest.TestSuite()
suite.addTest(BaiduTest('test_baidu_news'))
suite.addTest(BaiduTest('test_baidu_map'))
unittest.TextTestRunner(verbosity=2).run(suite)'''
注:
首先需要导入InitTest_demo模块中的InitTest类,测试类BaiduTest继承InitTest类。
先执行setUp方法,再执行具体的测试用例,最后执行tearDown方法。
python的类继承方式解决了再每个测试类中都需要编写测试固件的问题。把测试固件分离出去后,即使后期测试地址发生变化。只需要修改InitTest_demo模块中InitTest类中的地址即可,而不需要再每个测试类中修改测试地址,减少了编写重复性代码的开销。
3.测试断言
unittest常用的断言方法
1.assertEqual(self, first, second, msg=None)
--判断两个参数相等:first == second
2.assertNotEqual(self, first, second, msg=None)
--判断两个参数不相等:first != second
3.assertIn(self, member, container, msg=None)
--判断是字符串是否包含:member in container
4.assertNotIn(self, member, container, msg=None)
--判断是字符串是否不包含:member not in container
5.assertTrue(self, expr, msg=None)
--判断是否为真:expr is True
6.assertFalse(self, expr, msg=None)
--判断是否为假:expr is False
7.assertIsNone(self, obj, msg=None)
--判断是否为None:obj is None
8.assertIsNotNone(self, obj, msg=None)--判断是否不为None:obj is not None
实例:assertEqual(a, b),其它的断言方法请自行尝试)
assertEqual:两个值是否相等
self.assertEqual(self.driver.title, '百度一下,你就知道').encode('gbk')
以上代码回报错,self.driver.title获取的内容是“百度一下,你就知道”,他的类型是str类型;而“百度一下,你就知道”被encode转换成bytes类型,内容一致,类型不一致所以报错。
self.assertEqual(self.driver.title, '百度一下,你就知道')
注:两个断言内容内容一致,类型页一致
import unittest
from selenium import webdriver
class InitTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver = webdriver.Chrome()
cls.driver.maximize_window()
cls.driver.implicitly_wait(30)
url = 'http://www.baidu.com'
cls.driver.get(url)
@classmethod
def tearDownClass(cls):
cls.driver.quit()
print('end')
def test_baidu_title(self):
self.assertEqual(self.driver.title, '百度一下,你就知道')
print("self.driver.title:{0}".format(self.driver.title))
if __name__ == '__main__':
unittest.main(verbosity=2)
2.assertTrue:返回的是布尔类型
他主要对返回的测试结果执行布尔类型的校验。例如:验证百度搜索输入框是否可编辑,is_enadled 方法返回的结果是True。
import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By as b
class InitTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver = webdriver.Chrome()
cls.driver.maximize_window()
cls.driver.implicitly_wait(30)
url = 'http://www.baidu.com'
cls.driver.get(url)
@classmethod
def tearDownClass(cls):
cls.driver.quit()
print('end')
def test_baidu_title(self):
self.assertEqual(self.driver.title, '百度一下,你就知道')
print("self.driver.title:{0}".format(self.driver.title))
self.assertTrue(self.driver.title)
so = self.driver.find_element(b.ID, 'kw')
self.assertTrue(so.is_enabled())
if __name__ == '__main__':
unittest.main(verbosity=2)
3.assertFalse:返回的布尔类型进行校验
assertTrue和assertFalse,都是对返回的布尔类型进行校验。不同的是 assertFalse要求返回的结果是false,测试用例才会执行成功。以新浪邮箱登录页面为例,取消“自动登录”按钮后,方法is_elected返回的结果是False。
import unittest
import time as t
from selenium import webdriver
from selenium.webdriver.common.by import By as b
class InitTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver = webdriver.Chrome()
cls.driver.maximize_window()
cls.driver.implicitly_wait(30)
url = 'https://mail.sina.com.cn/'
cls.driver.get(url)
@classmethod
def tearDownClass(cls):
cls.driver.quit()
print('end')
def test_baidu_news(self):
'''验证:新浪邮箱登录页面自动取消自动登录'''
isautoLogin = self.driver.find_element(b.ID, 'store1')
isautoLogin.click()
t.sleep(3)
# 是否选中元素
self.assertFalse(isautoLogin.is_selected())
t.sleep(3)
if __name__ == '__main__':
unittest.main(verbosity=2)
4.assertIn:指的是一个值是否包含另外一个值的范围内。
报错:
import unittest
import time as t
from selenium import webdriver
from selenium.webdriver.common.by import By as b
class InitTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver = webdriver.Chrome()
cls.driver.maximize_window()
cls.driver.implicitly_wait(30)
url = 'https://mail.sina.com.cn/'
cls.driver.get(url)
@classmethod
def tearDownClass(cls):
cls.driver.quit()
print('end')
def test_baidu_news(self):
'''验证:新浪邮箱登录页面自动取消自动登录'''
'''isautoLogin = self.driver.find_element(b.ID, 'store1')
isautoLogin.click()
t.sleep(3)
# 是否选中元素
self.assertFalse(isautoLogin.is_selected())
t.sleep(3)'''
self.assertIn(self.driver.current_url, 'http://www.baidu.com')
print("False")
if __name__ == '__main__':
unittest.main(verbosity=2)
修改后代码:
import unittest
import time as t
from selenium import webdriver
from selenium.webdriver.common.by import By as b
class InitTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver = webdriver.Chrome()
cls.driver.maximize_window()
cls.driver.implicitly_wait(30)
url = 'https://mail.sina.com.cn/'
cls.driver.get(url)
@classmethod
def tearDownClass(cls):
cls.driver.quit()
print('end')
def test_baidu_news(self):
'''验证:新浪邮箱登录页面自动取消自动登录'''
'''isautoLogin = self.driver.find_element(b.ID, 'store1')
isautoLogin.click()
t.sleep(3)
# 是否选中元素
self.assertFalse(isautoLogin.is_selected())
t.sleep(3)'''
print("self.driver.current_url:{0}".format(self.driver.current_url))
self.assertIn(self.driver.current_url, 'https://mail.sina.com.cn/')
print("False")
if __name__ == '__main__':
unittest.main(verbosity=2)
4.断言的注意事项
不正确的if应用
import unittest
import time as t
import self as self
from selenium import webdriver
from selenium.webdriver.common.by import By as b
class InitTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver = webdriver.Chrome()
cls.driver.maximize_window()
cls.driver.implicitly_wait(30)
url = 'https://mail.sina.com.cn/'
cls.driver.get(url)
@classmethod
def tearDownClass(cls):
cls.driver.quit()
print('end')
def test_baidu_news(self):
'''验证:新浪邮箱登录页面自动取消自动登录'''
isa = self.driver.find_element(b.ID, 'storel')
if isa.is_enabled():
print("succcess")
else:
print("fail")
if __name__ == '__main__':
unittest.main(verbosity=2)
注:
一个测试用例只有两种结果 Pass,要么Fail(代码错误也显示Fail)。以上代码不管复选框是不是自动选中的,测试执行结果都是 Pass,测试用例都是通过的。因此,在自动化测试的测试用例中,切记不要使用 if else 这类判断代码来代替断言。
不正确的异常应用
import unittest
import time as t
import self as self
from selenium import webdriver
from selenium.webdriver.common.by import By as b
class InitTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver = webdriver.Chrome()
cls.driver.maximize_window()
cls.driver.implicitly_wait(30)
url = 'https://mail.sina.com.cn/'
cls.driver.get(url)
@classmethod
def tearDownClass(cls):
cls.driver.quit()
print('end')
def test_baidu_news(self):
'''验证:新浪邮箱登录页面自动取消自动登录'''
isa = self.driver.find_element(b.ID, 'storel')
try:
self.assertTrue(isa.is_selected())
except:
print("fail")
if __name__ == '__main__':
unittest.main(verbosity=2)
注:
以上代码和应用 if else 的结果一样,即使自动登录未被选中,测试用例的结果也显示 Pass。另外,在自动化测试中尽量不要应用打印结果来判断测试用例的情况,用例如果在代码错误或者功能有bug的情况下就让用例报错或者失败,而不是显示 Pass,只有功能正常的测试用例结果才是 Pass的。
5.批量执行测试用例
1.testCase包中 test_baidu.py和test_sina.py 两个文件。
2.创建 allTest.py ,在allTest.py文件中编写批量执行的代码。
test_baidu.py
import unittest
import time as t
import self as self
from selenium import webdriver
from selenium.webdriver.common.by import By as b
class InitTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver = webdriver.Chrome()
cls.driver.maximize_window()
cls.driver.implicitly_wait(30)
url = 'https://www.baidu.com'
cls.driver.get(url)
@classmethod
def tearDownClass(cls):
cls.driver.quit()
print('end')
def test_baidu_title(self):
self.assertEqual(self.driver.title, '百度一下,你就知道')
if __name__ == 'main':
unittest.main(verbosity=2)
test_sina.py
import unittest
import time as t
import self as self
from selenium import webdriver
from selenium.webdriver.common.by import By as b
class InitTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver = webdriver.Chrome()
cls.driver.maximize_window()
cls.driver.implicitly_wait(30)
url = 'https://mail.sina.com.cn/'
cls.driver.get(url)
@classmethod
def tearDownClass(cls):
cls.driver.quit()
print('end')
def test_username_password_null(self):
""" 验证:新浪登录页面用户和密码为空错误提示信息 """
self.driver.find_element(b.ID, 'freename').send_keys("")
self.driver.find_element(b.ID, 'freepassword').send_keys("")
self.driver.find_element(b.LINK_TEXT, '登录').click()
de = self.driver.find_element(b.XPATH, '/html/body/div[1]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]').text
self.assertEqual(de, '请输入邮箱名')
if __name__ == 'main':
unittest.main(verbosity=2)
allTest.py
import unittest
import os
def allCases():
""" 获取所有测试模块 """
suite=unittest.TestLoader().discover(start_dir=os.path.dirname(__file__), pattern='test_*.py', top_level_dir=None)
return suite
if __name__ == 'main':
unittest.TextTestRunner(verbosity=2).run(allCases())
注:
在以上代码中,批量获取测试模块用到的方法是 discover。
discover 方法有三个参数:start_dir 是测试模块的路径,存放在 testCase包中;pattern 用来获取testCase包中所有以test开头的模块文件,会获取到test_baidu.py和test_sina.py;top_level_dir 在调用的时候直接给默认值 None。
discover代码如下:
def discover(self, start_dir, pattern='test*.py', top_level_dir=None):
pass
运行结果:
6.生成测试报告
1.运行allTest.py文件得到的测试结果不够专业,无法直接提交,因此需要借助第三方库生成HTML格式的测试报告。这里用到的库是 HTMLTestRunner.py
2.创建 report 文件夹,与testCase 包放在同一个目录下,继续完善allTest.py 文件,最终生成测试报告。
start_dir=os.path.dirname(__file__)
1. import os
2. #该文件所在位置:D:\第1层\第2层\第3层\第4层\第5层\test11.py
3. path1 = os.path.dirname(__file__)
4. print(path1)#获取当前运行脚本的绝对路径
5. path2 = os.path.dirname(os.path.dirname(__file__)) #
6. print(path2)#获取当前运行脚本的绝对路径(去掉最后一个路径)
7. path3 = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
8. print(path3)#获取当前运行脚本的绝对路径(去掉最后2个路径)
9. path4 = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
10. print(path4)#获取当前运行脚本的绝对路径(去掉最后3个路径)
11. path5 = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))
12. print(path5)#获取当前运行脚本的绝对路径(去掉最后4个路径)
13. path6 = os.__file__ #获取os所在的目录
14. print(path6)
16. C:\Python352\python.exe D:/第1层/第2层/第3层/第4层/第5层/test11.py
17. D:/第1层/第2层/第3层/第4层/第5层
18. D:/第1层/第2层/第3层/第4层
19. D:/第1层/第2层/第3层
20. D:/第1层/第2层
21. D:/第1层
完善allTest.py
import unittest
import os
import HTMLTestRunner
import time as t
def allCases():
""" 获取所有测试模块 """
suite = unittest.defaultTestLoader.discover(start_dir=os.path.join(os.path.dirname(__file__)), pattern='test*.py',
top_level_dir=None)
return suite
def getNowTime():
"""获取当前时间"""
return t.strftime('%Y-%m-%d %H_%M_%S', t.localtime(t.time()))
def run():
fileName = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'report', getNowTime() + 'report.html')
report_path = open(fileName, mode='w')
runner = HTMLTestRunner.HTMLTestRunner(report_path, title=u"UI自动化测试报告", description=u"UI自动化测试报告详细信息")
runner.run(allCases())
report_path.close()
if __name__ == '__main__':
run()
7.代码覆盖率统计实战:Coverage.py
1.安装 coverage 后运行 allTest.py 文件,程序会运行所有以 test 开头的测试模块的文件。
coverage run allTest.py
2.再次执行coverage html,也就是代码覆盖率统计,通过html的文件查看
执行后,会在testCase 包下生成一个 htmlcov 文件夹,在该文件夹里面显示的是代码覆盖率统计的文件