python+单元测试框架unittest

本文深入探讨了使用Python的unittest框架进行自动化测试的方法,包括测试固件的设置与清理、构建测试套件的不同策略、测试断言的正确使用,以及如何批量执行测试用例并生成详细的测试报告。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. 测试固件每次都执行,setUp和tearDown的测试固件
from selenium import webdriver
import unittest
import time


class BaiduTest(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get('http://www.baidu.com')
        self.driver.implicitly_wait(30)

    def tearDown(self):
        self.driver.quit()

    def test_baidu_news(self):
        '''验证:测试百度首页点击新闻后的跳转'''
        self.driver.find_element_by_link_text('新闻').click()
        time.sleep(2)

    def test_baidu_map(self):
        '''验证:测试百度首页点击地图后的跳转'''
        self.driver.find_element_by_link_text('地图').click()
        time.sleep(2)


if __name__ == '__main__':
    unittest.main(verbosity=2)
  1. 测试固件只执行一次
from selenium import webdriver
import unittest
import time


class BaiduTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()
        cls.driver.maximize_window()
        cls.driver.get('http://www.baidu.com')
        cls.driver.implicitly_wait(30)

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()

    def test_baidu_news(self):
        '''验证:测试百度首页点击新闻后的跳转'''
        self.driver.find_element_by_link_text('新闻').click()
        time.sleep(2)
        self.driver.get('http://www.baidu.com')

    def test_baidu_map(self):
        '''验证:测试百度首页点击地图后的跳转'''
        self.driver.find_element_by_link_text('地图').click()
        time.sleep(2)
        self.driver.get('http://www.baidu.com')


if __name__ == '__main__':
    unittest.main(verbosity=2)
  1. 构建测试套件
    3.1 测试用例按顺序执行
    要用到TestSuite类的addTest()方法
from selenium import webdriver
import unittest


class BaiduTest(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get('http://www.baidu.com')
        self.driver.implicitly_wait(30)

    def tearDown(self):
        self.driver.quit()

    def test_baidu_news(self):
        '''验证:测试百度首页点击新闻后的跳转'''
        self.driver.find_element_by_link_text('新闻').click()
        url = self.driver.current_url
        self.assertEqual(url, 'http://news.baidu.com/')

    def test_baidu_map(self):
        '''验证:测试百度首页点击地图后的跳转'''
        self.driver.find_element_by_link_text('地图').click()
        self.driver.get('http://www.baidu.com')


if __name__ == '__main__':
    suite = unittest.TestSuite()
    suite.addTest(BaiduTest('test_baidu_news'))
    suite.addTest(BaiduTest('test_baidu_map'))
    runner = unittest.TextTestRunner(verbosity=2)
    runner.run(suite)

默认的执行顺序是TestCaseName的ASCII码顺序,调用addTest()方法后,先添加的先执行,后添加的后执行。
但是这里addTest方法没有起到作用,还是先执行test_baidu_map,后执行test_baidu_news。原因是:在pycharm中,引入了unittest模块,会默认按照unittest模式执行。需要将unittest模式转换成普通模式。 解决方法见文章:https://blog.csdn.net/qq_33356414/article/details/94556140

3.2 按测试类执行
用makeSuite方法,把所有的测试用例组成测试套件,避免一个一个添加测试用例的繁琐过程。

from selenium import webdriver
import unittest


class BaiduTest(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get('http://www.baidu.com')
        self.driver.implicitly_wait(30)

    def tearDown(self):
        self.driver.quit()

    def test_baidu_news(self):
        '''验证:测试百度首页点击新闻后的跳转'''
        self.driver.find_element_by_link_text('新闻').click()
        url = self.driver.current_url
        self.assertEqual(url, 'http://news.baidu.com/')

    def test_baidu_map(self):
        '''验证:测试百度首页点击地图后的跳转'''
        self.driver.find_element_by_link_text('地图').click()
        self.driver.get('http://www.baidu.com')


if __name__ == '__main__':
    suite = unittest.TestSuite(unittest.makeSuite(BaiduTest))
    unittest.TextTestRunner(verbosity=2).run(suite)

3.3 加载测试类
suite = unittest.TestLoader().loadTestsFromTestCase(BaiduTest) 用TestLoader()类加载。

from selenium import webdriver
import unittest


class BaiduTest(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get('http://www.baidu.com')
        self.driver.implicitly_wait(30)

    def tearDown(self):
        self.driver.quit()

    def test_baidu_news(self):
        '''验证:测试百度首页点击新闻后的跳转'''
        self.driver.find_element_by_link_text('新闻').click()
        url = self.driver.current_url
        self.assertEqual(url, 'http://news.baidu.com/')

    def test_baidu_map(self):
        '''验证:测试百度首页点击地图后的跳转'''
        self.driver.find_element_by_link_text('地图').click()
        self.driver.get('http://www.baidu.com')


if __name__ == '__main__':
    suite = unittest.TestLoader().loadTestsFromTestCase(BaiduTest)
    unittest.TextTestRunner(verbosity=2).run(suite)

3.4 按测试模块执行
suite = unittest.TestLoader().loadTestsFromModule(‘sy_unittest.py’)

from selenium import webdriver
import unittest


class BaiduTest(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get('http://www.baidu.com')
        self.driver.implicitly_wait(30)

    def tearDown(self):
        self.driver.quit()

    def test_title(self):
        """验证:测试百度浏览器的title"""
        self.assertEqual(self.driver.title, '百度一下,你就知道')

    def test_002(self):
        """验证:测试百度首页点击新闻后的跳转"""
        self.driver.find_element_by_link_text('新闻').click()
        url = self.driver.current_url
        self.assertEqual(url, 'http://news.baidu.com/')

    @unittest.skip('do not run')
    def test_baidu_003(self):
        """验证:测试百度首页点击地图后的跳转"""
        self.driver.find_element_by_link_text('地图').click()


class BaiduMap(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get('http://www.baidu.com')
        self.driver.implicitly_wait(30)

    def tearDown(self):
        self.driver.quit()

    def test_baidu_map(self):
        """验证:测试百度首页点击地图后的跳转"""
        self.driver.find_element_by_link_text('地图').click()
        self.driver.get('http://www.baidu.com')


if __name__ == '__main__':
    suite = unittest.TestLoader().loadTestsFromModule('sy_unittest.py')
    unittest.TextTestRunner(verbosity=2).run(suite)

3.5 优化测试套件
可以单独把测试套件写成一个方法来调用。

from selenium import webdriver
import unittest


class BaiduTest(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get('http://www.baidu.com')
        self.driver.implicitly_wait(30)

    def tearDown(self):
        self.driver.quit()

    def test_news(self):
        """验证:测试百度首页点击新闻后的跳转"""
        self.driver.find_element_by_link_text('新闻').click()
        url = self.driver.current_url
        self.assertEqual(url, 'http://news.baidu.com/')

    def test_baidu_map(self):
        """验证:测试百度首页点击地图后的跳转"""
        self.driver.find_element_by_link_text('地图').click()
        self.driver.get('http://www.baidu.com')

    @staticmethod
    def suite(testCaseClass):
        suite = unittest.TestLoader().loadTestsFromTestCase(testCaseClass)
        return suite


if __name__ == '__main__':
    unittest.TextTestRunner(verbosity=2).run(BaiduTest.suite(BaiduTest))
  1. 分离测试固件
    测试固件用来在测试开始时初始化WebDriver类及打开浏览器,在测试结束后关闭浏览器。可以单独分离出来写,新建init.py模块,类名称为InitTest。
import unittest
from selenium import webdriver


class InitClass(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get('http://www.baidu.com')
        self.driver.implicitly_wait(30)

    def tearDown(self):
        self.driver.quit()

将测试固件分离出来后,测试类继承InitTest,在测试类的文件中直接编写要执行的测试用例。

import unittest
from init import InitClass


class BaiduTest(InitClass):
    def test_news(self):
        """验证:测试百度首页点击新闻后的跳转"""
        self.driver.find_element_by_link_text('新闻').click()
        url = self.driver.current_url
        self.assertEqual(url, 'http://news.baidu.com/')

    def test_baidu_map(self):
        """验证:测试百度首页点击地图后的跳转"""
        self.driver.find_element_by_link_text('地图').click()
        self.driver.get('http://www.baidu.com')

    @staticmethod
    def suite(testCaseClass):
        suite = unittest.TestLoader().loadTestsFromTestCase(testCaseClass)
        return suite


if __name__ == '__main__':
    unittest.TextTestRunner(verbosity=2).run(BaiduTest.suite(BaiduTest))

把测试固件分离的好处是,在后续的测试中,如果需要改变测试地址,只需要修改init模块中的URL地址即可,减少了编写重复性代码的开销。
5. 测试断言
断言就是判断实际测试结果是否与预期结果一致。每一个测试用例必须要有断言。
在TestCase类中提供了assert方法来检查和报告失败。
assertEqual(a, b)方法用于测试两个值是否相等,不仅要内容相同还要类型相同。
assertTrue(a) 验证布尔类型,要求返回的是True
assertFalse(a) 验证布尔类型,要求返回的是False
assertIn(a,b) 判断a是否包含在b内
6. 断言的注意事项
注意⚠️在自动化测试中,尽量不要通过打印结果来判断测试用例的情况。用例如果错误或者功能有bug的情况下就让用例报错失败,只有功能正常正确的测试用例结果才会是pass的。
切记不要使用if…else判断来代替断言,因为这样不管是if else的哪种情况,测试用例都会通过,显示pass
也切记不要用try…except异常来判断用例,这样结果总会是pass.

  1. 批量执行测试用例
    新建文件allTests.py,在allTests.py中编写批量执行的代码。test_baidu.py和test_sina.py,放在allTests.py同一个文件路径下。
    test_baidu.py文件如下:
from selenium import webdriver
import unittest


class BaiduTest(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get('http://www.baidu.com')
        self.driver.implicitly_wait(30)

    def tearDown(self):
        self.driver.quit()

    def test_baidu_news(self):
        '''验证:测试百度首页点击新闻后的跳转'''
        self.driver.find_element_by_link_text('新闻').click()
        url = self.driver.current_url
        self.assertEqual(url, 'http://news.baidu.com/')


if __name__ == '__main__':
    suite = unittest.TestSuite()
    suite.addTest(BaiduTest('test_baidu_news'))
    suite.addTest(BaiduTest('test_baidu_map'))
    runner = unittest.TextTestRunner(verbosity=2)
    runner.run(suite)

test_sina.py文件如下:

from selenium import webdriver
import unittest


class SinaTest(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get('http://mail.sina.com.cn/')
        self.driver.implicitly_wait(30)

    def tearDown(self):
        self.driver.quit()

    def test_username_password_null(self):
        """验证:新浪登录页面用户名和密码为空提示错误信息"""
        self.driver.find_element_by_id('freename').send_keys('')
        self.driver.find_element_by_id('freepassword').send_keys('')
        self.driver.find_element_by_link_text('登录').click()

        divError = self.driver.find_element_by_xpath('/html/body/div[1]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]').text
        self.assertEqual(divError, '请输入邮箱名')


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

allTests.py文件如下:

import unittest
import os


def allCases():
    """获取所有的测试"""
    suite = unittest.TestLoader().discover(start_dir=os.path.dirname(__file__), pattern='test_*.py', top_level_dir=None)
    # os.path.dirname(__file__) 表示获取当前文件的路径
    return suite


if __name__ == '__main__':
    unittest.TextTestRunner(verbosity=2).run(allCases())

批量获取测试用例,用到的是discover()方法,第一个参数start_dir表示测试模块的路径,os.path.dirname(__file__) 表示获取当前文件的路径, 第二个参数pattern用来获取testCase包中所有以test开头的模块文件;第三个参数top_level_dir默认值为None.
运行allTests.py文件,得到的结果如图。
在这里插入图片描述
8. 生成测试报告
生成测试报告,需要借助第三方库生成HTML格式的测试报告,这里用的库是HTMLTestRunner.py, 下载地址http://tungwaiyip.info/software/HTMLTestRunner.html
下载完HTMLTestRunner.html文件后,将该文件放到Python安装路径的Lib文件夹中。(这里直接放到了测试文件同一个目录下)
在testCase同一个目录下,创建文件夹report,用来存放生成的测试报告。
注意⚠️上面的HTMLTestRunner文件是用于python2的,在python3中会报很多错,在网上找到了这份适用于python3的:https://pan.baidu.com/s/1W6e_Bqg9dZTkVOWUP93XkA,放到目录下后,运行成功。
在这里插入图片描述
修改allTests.py文件

import unittest
import os
import HTMLTestRunner
import time


def allCases():
    """获取所有的测试"""
    suite = unittest.TestLoader().discover(start_dir=os.path.dirname(__file__), pattern='test_*.py', top_level_dir=None)
    # os.path.dirname(__file__) 表示获取当前文件的路径
    return suite


def getNowTime():
    """获取当前时间"""
    return time.strftime('%Y-%m-%d %H_%M_%S', time.localtime(time.time()))


def run():
    fileName = os.path.join(os.path.dirname(__file__), 'report', getNowTime()+'report.html')
    fp = open(fileName, 'wb')
    runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title='UI自动化测试报告', description='UI自动化测试报告详细信息')
    runner.run(allCases())


if __name__ == '__main__':
    run()

allTests.py文件中导入了库os、HTMLTestRunner库。run()方法用来执行测试套件中的测试用例,并且生成测试报告。
##以下可以忽略。下载了python3版本的HTMLTestRunner.html文件就没有问题了。
运行之后,报错ModuleNotFoundError: No module named ‘StringIO’,
显示没有安装StringIO模块,在pycharm中安装,但是查不到,发现可能是和版本有关。
在这里插入图片描述
在python 2.x版本中,import StringIO可以直接导入,但是python3中,StringIO已经不存在了,要这样写:

from io import StringIO

改好后运行,又报错了:AttributeError: type object ‘_io.StringIO’ has no attribute ‘StringIO’
在这里插入图片描述
生成的测试报告如图:
在这里插入图片描述
9. 代码覆盖率统计
安装coverage模块,运行coverage3 run allTests.py命令,再运行coverage html命令。
在这里插入图片描述就可以生成覆盖率统计报告了。
在这里插入图片描述
打开index.html文件可以看到所有file的覆盖率总览。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值