unittest单元测试框架

一、认识unittest

1.认识单元测试

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

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

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

    def mul(self):
        return self.a * self.b

    def div(self):
        return self.a / self.b
import unittest
from calculator import Calculator


# 创建一个测试类必须要继承unittest模块的TestCase类
class MyTestCase(unittest.TestCase):
    # 创建一个测试方法必须以test开头
    def test_add(self):
        c = Calculator(3, 5)
        result = c.add()
        self.assertEqual(result, 8)

    def test_sub(self):
        c = Calculator(3, 5)
        result = c.sub()
        self.assertEqual(result, -2)

    def test_mul(self):
        c = Calculator(3, 5)
        result = c.mul()
        self.assertEqual(result, 10)

    def test_div(self):
        c = Calculator(10, 5)
        result = c.div()
        self.assertEqual(result, 2)


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

 2.重要概念

2.1 Test Case 

是最小的测试单元,用于检查特定输入集合的特定返回值。unittest提供了TestCase基类。

2.2 Test Suite

测试套件是测试用例、测试套件或两者的集合,用于组装一组要运行的测试。unittest提供了TestSuite类来创建测试套件。

2.3 Test Runner

是一个组件,用于协调测试的执行并向用户提供结果。unittest提供了TextTestRunner类运行测试用例。

2.4 Test Fixture

代表执行一个或多个测试所需的环境设备,以及关联的清理动作。

import unittest
from calculator import Calculator


# 创建一个测试类必须要继承unittest模块的TestCase类
class TestCalculator(unittest.TestCase):
    # 测试用例的前置动作
    def setUp(self):
        print("test start:")

    # 测试用例的后置动作
    def tearDown(self):
        print("test end")

    # 创建一个测试方法必须以test开头
    def test_add(self):
        c = Calculator(3, 5)
        result = c.add()
        self.assertEqual(result, 8)

    def test_sub(self):
        c = Calculator(3, 5)
        result = c.sub()
        self.assertEqual(result, -2)

    def test_mul(self):
        c = Calculator(3, 5)
        result = c.mul()
        self.assertEqual(result, 10)

    def test_div(self):
        c = Calculator(10, 5)
        result = c.div()
        self.assertEqual(result, 2)


if __name__ == '__main__':
    # 创建测试套件
    suit = unittest.TestSuite()
    suit.addTest(TestCalculator("test_add"))
    suit.addTest(TestCalculator("test_sub"))
    suit.addTest(TestCalculator("test_mul"))
    suit.addTest(TestCalculator("test_div"))

    # 创建测试运行程序
    runner = unittest.TextTestRunner()
    runner.run(suit)

3.断言方法

import unittest


class TestAssert(unittest.TestCase):

    def test_equal(self):
        self.assertEqual(2+2, 4)
        self.assertEqual("python", "python")
        self.assertNotEqual("hello", "love")

    def test_in(self):
        self.assertIn("hello", "hello world")
        self.assertNotIn("hi", "hello")

    def test_true(self):
        self.assertTrue(True)
        self.assertFalse(False)


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

4.测试用例的组织与discover方法

class LeapYear:

    def __init__(self, year):
        self.year = year

    def answer(self):
        year = self.year
        if year % 100 == 0:
            if year % 400 == 0:
                return "{0}是闰年".format(year)
            else:
                return "{0}不是闰年".format(year)
        else:
            if year % 4 == 0:
                return "{0}是闰年".format(year)
            else:
                return "{0}不是闰年".format(year)
import unittest
from leapyear import LeapYear


class TestLeapYear(unittest.TestCase):

    def test_2000(self):
        ly = LeapYear(2000)
        self.assertEqual(ly.answer(), "2000是闰年")
    def test_2001(self):
        ly = LeapYear(2001)
        self.assertEqual(ly.answer(), "2001是闰年")


if __name__ == "__main__":
    unittest.main()
import unittest

# 定义测试用例的目录为当前目录中的test_case目录
test_dir = './test_case'
suits = unittest.defaultTestLoader.discover(test_dir, pattern='test*.py')

if __name__ == "__main__":
    runner = unittest.TextTestRunner()
    runner.run(suits)

二、关于unittest

1. 测试用例的执行顺序

unittest默认根据ASCII码的顺序加载测试用例(0~9,A~Z,a~z),可以通过使用套件来控制。

2.执行多级目录的测试用例

discover()方法只能加载写在方法里的目录的用例,可以通过给每个子目录下放一个__init__.py文件将一个子目录标记成一个标准的Python模块。

3.跳过测试和预期失败

import unittest


class MyTest(unittest.TestCase):

    @unittest.skip("直接跳过测试")
    def test_skip(self):
        print("aaa")

    @unittest.skipIf(3 > 2, "当条件为真时跳过测试")
    def test_skip_if(self):
        print("bbb")

    @unittest.skipUnless(3 > 2, "当条件为真时执行测试")
    def test_skip_unless(self):
        print("ccc")
    # 不管执行结果如何都会标记失败,但不会抛出失败信息
    @unittest.expectedFailure
    def test_expect_failure(self):
        self.assertEqual(2, 3)


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

 4.Fixture

即setUp/tearDown,还有测试类和模块的Fixture

import unittest


def setUpModule():
    print("test Module start >>>>>")

def tearDownModule():
    print("test module end >>>>>")


class MyTest(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        print("test class start >>>>>")

    @classmethod
    def tearDownClass(cls):
        print("test class end >>>>>")

    def setUp(self):
        print("test case start >>>>>")

    def tearDown(self):
        print("test case end >>>>>")

    def test_case1(self):
        print("case1")

    def test_case2(self):
        print("case2")


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

三、编写Web自动化测试

import unittest
from time import sleep
from selenium import webdriver


class TestBaidu(unittest.TestCase):

    # 可以避免多次打开浏览器
    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()
        cls.base_url = "https://www.baidu.com"
    
    # 不是用例不会执行
    def baidu_search(self, search_key):
        self.driver.get(self.base_url)
        self.driver.find_element_by_id("kw").send_keys(search_key)
        self.driver.find_element_by_id("su").click()
        sleep(2)

    def test_search_key_selenium(self):
        search_key = "selenium"
        self.baidu_search(search_key)
        self.assertEqual(self.driver.title, search_key+"_百度搜索")

    def test_search_unittest(self):
        search_key = "unittest"
        self.baidu_search(search_key)
        self.assertEqual(self.driver.title, search_key+"_百度搜索")

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


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

四、unittest扩展

1.HTML测试报告

1.1 安装HTMLTestRunner

可直接下载下来放在python38的Lib目录下

修改:

        将import StringIO修改成import io
        将self.outputBuffer = StringIO.StringIO()修改成self.outputBuffer = io.StringIO()
        将print >> sys.stderr, ‘\nTime Elapsed: %s’ % (self.stopTime-self.startTime)
修改成print(sys.stderr, ‘\nTime Elapsed: %s’ % (self.stopTime-self.startTime))
        将if not rmap.has_key(cls):修改成if not cls in rmap:
        将uo = o.decode(‘latin-1’)修改成uo = e
        将ue = e.decode(‘latin-1’)修改成ue = e

1.2 生成HTML测试报告

import unittest
from TestRunner import HTMLTestRunner
import time

# 定义测试用例的目录为当前目录中的test_case目录
test_dir = './test_case'
suits = unittest.defaultTestLoader.discover(test_dir, pattern='test*.py')

if __name__ == "__main__":
    now_time = time.strftime("%Y-%m-%d %H_%M_%S")
    fp = open('./test_report/'+ now_time + 'result.html', 'wb')
    runner = HTMLTestRunner(stream=fp, title="百度搜索测试报告", description="运行环境:Windows 10, Chrome浏览器")
    runner.run(suits)
    fp.close()

 并在每个类和方法下main添加对应的注释。

 

 2.数据驱动应用

2.1 数据驱动

baidu_data.csv

name,search_key
case1,selenium
case2,unittest
case3,parameterized
import csv
import codecs
import unittest
from time import sleep
from itertools import islice
from selenium import webdriver


class TestBaidu(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()
        cls.base_url = "http://www.baidu.com"
        cls.test_data = []
        with codecs.open('baidu_data.csv', 'r', 'utf_8_sig') as f:
            data = csv.reader(f)
            for line in islice(data, 1, None):
                cls.test_data.append(line)

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

    def baidu_search(self, search_key):
        self.driver.get(self.base_url)
        self.driver.find_element_by_id("kw").send_keys(search_key)
        self.driver.find_element_by_id("su").click()
        sleep(3)

    def test_search_selenium(self):
        self.baidu_search(self.test_data[0][1])

    def test_search_unittest(self):
        self.baidu_search(self.test_data[1][1])

    def test_search_parameterized(self):
        self.baidu_search(self.test_data[2][1])


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

 

 2.2 Parameterized(Python的一个参数化库)

import unittest
from time import sleep
from selenium import webdriver
from parameterized import parameterized


class TestBaidu(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()
        cls.base_url = 'http://www.baidu.com'

    def baidu_search(self, search_key):
        self.driver.get(self.base_url)
        self.driver.find_element_by_id("kw").send_keys(search_key)
        self.driver.find_element_by_id("su").click()
        sleep(2)
    # 通过Parameterized实现参数化
    # @parameterized.expand()中每个元组被认为是一条测试用例
    @parameterized.expand([
            ("case1", "selenium"),
            ("case2", "unittest"),
            ("case3", "parameterized"),
        ])
    def test_search(self, name, search_key):
        self.baidu_search(search_key)
        self.assertEqual(self.driver.title, search_key + "_百度搜索")

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


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

 2.3 DDT(允许使用不同的测试数据运行一个测试用例,并将其展示为多个测试用例)

import unittest
from time import sleep
from selenium import webdriver
from ddt import ddt, data, file_data, unpack


@ddt
class TestBaidu(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()
        cls.base_url = "http://www.baidu.com"

    def baidu_search(self, search_key):
        self.driver.get(self.base_url)
        self.driver.find_element_by_id("kw").send_keys(search_key)
        self.driver.find_element_by_id("su").click()
        sleep(2)

    # 参数化方式一(列表)
    @data(["case1", "selenium"], ["case2", "ddt"], ["case3", "python"])
    @unpack
    def test_search1(self, case, search_key):
        print("第一组测试用例: ", case)
        self.baidu_search(search_key)
        self.assertEqual(self.driver.title, search_key + "_百度搜索")

    # 参数化方式二(元组)
    @data(("case1", "selenium"), ("case2", "ddt"), ("case3", "python"))
    @unpack
    def test_search2(self, case, search_key):
        print("第二组测试用例: ", case)
        self.baidu_search(search_key)
        self.assertEqual(self.driver.title, search_key + "_百度搜索")

    # 参数化方式三(字典),字典的key与测试方法的参数要保持一致
    @data({"search_key": "selenium"}, {"search_key": "ddt"}, {"search_key": "python"})
    @unpack
    def test_search3(self, search_key):
        print("第三组测试用例: ", search_key)
        self.baidu_search(search_key)
        self.assertEqual(self.driver.title, search_key + "_百度搜索")

    # 参数化读取JSON文件
    @file_data('ddt_data_file.json')
    def test_search4(self, search_key):
        print("第四组测试用例:", search_key)
        self.baidu_search(search_key)
        self.assertEqual(self.driver.title, search_key + "_百度搜索")

    # 参数化读取yaml文件
    @file_data('ddt_data_file.yaml')
    def test_search5(self, case):
        search_key = case[0]["search_key"]
        print("第五组测试用例:", search_key)
        self.baidu_search(search_key)
        self.assertEqual(self.driver.title, search_key + "_百度搜索")

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


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

 ddt_data_file.json

{
  "case1": {"search_key":  "python"},
  "case2": {"search_key":  "ddt"},
  "case3": {"search_key":  "selenium"}
}

ddt_data_file.yaml

case1:
  - search_key: "python"
case2:
  - search_key: "ddt"
case3:
  - search_key: "unittest"

3.自动发送邮件功能

3.1 Python自带的发送邮件功能

发送邮件正文:

import smtplib
from email.mime.text import MIMEText
from email.header import Header

# 发送邮件主题
subject = 'Python email test'

# 编写HTML类型的邮件正文,MIMEText()定义发送邮件正文、格式、编码
msg = MIMEText('<html><h1>你好</h1></html>', 'html', 'utf-8')
# Header()定义邮件的主题和编码类型
msg['Subject'] = Header(subject, 'utf-8')

# 发送邮件
smtp = smtplib.SMTP()
# connect()方法指定连接的邮箱服务
smtp.connect("smtp.qq.com")
# login()方法指定登录邮箱的账号和密码
smtp.login("1125868410@qq.com", "密码")
# sendmail()方法指定发件人、收件人、邮件正文
smtp.sendmail("1125868410@qq.com", "202108591@qq.com", msg.as_string())
# 关闭邮件服务器连接
smtp.quit()

发送带附件的邮件:

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

# 发送邮件主题
subject = 'Python email test'

# 发送的附件
with open('../log.txt', 'rb') as f:
    send_att = f.read()

# 定义发送邮件的正文、格式、编码
att = MIMEText(send_att, 'text', 'utf-8')
# 指定附件内容类型
att["Content-Type"] = 'application/octet-stream'
# 指定附件的文件名
att["Content-Disposition"] = 'attachment; filename="log.txt"'

# 定义邮件主题
msg = MIMEMultipart()
msg['Subject'] = subject
# 指定附件信息
msg.attach(att)

# 发送邮件
smtp = smtplib.SMTP()
# connect()方法指定连接的邮箱服务
smtp.connect("smtp.qq.com")
# login()方法指定登录邮箱的账号和密码
smtp.login("1125868410@qq.com", "密码")
# sendmail()方法指定发件人、收件人、邮件正文
smtp.sendmail("1125868410@qq.com", "202108591@qq.com", msg.as_string())
# 关闭邮件服务器连接
smtp.quit()

3.2 用yagmail发送邮件

import yagmail

# 连接邮箱服务器
yag = yagmail.SMTP(user="1125868410@qq.com", password="密码", host="smtp.qq.com")

# 邮件正文
contents = ['This is the body, and here is just text http://somedomain/image.png',
            'You can find an audio file attached.']

# 发送邮件
yag.send('202108591@qq.com', 'subject', contents)
# 发送多人
yag.send(['receiver1', 'receiver2', '...'], 'subject', contents)
# 发送带附件的邮件
yag.send('202108591@qq.com', 'subject', contents, ['../log.txt'])

3.3 整合自动发邮件功能

import unittest
from TestRunner import HTMLTestRunner
import time
import yagmail


def send_mail(report):
    yag = yagmail.SMTP(user="1125868410@qq.com", password="密码", host="smtp.qq.com")
    subject = "主题, 自动化测试报告"
    contents = "正文,请查看附件。"
    yag.send('202108591@qq.com', subject, contents, report)
    print('email has send out!')


if __name__ == "__main__":
    # 定义测试用例的目录为当前目录中的test_case目录
    test_dir = './test_case'
    suits = unittest.defaultTestLoader.discover(test_dir, pattern='test*.py')
    # 获取当前的日期和时间
    now_time = time.strftime("%Y-%m-%d %H_%M_%S")
    html_report = './test_report/'+ now_time + 'result.html'
    fp = open(html_report, 'wb')
    # 调用HTMLTestRunner运行测试用例
    runner = HTMLTestRunner(stream=fp, title="百度搜索测试报告", description="运行环境:Windows 10, Chrome浏览器")
    runner.run(suits)
    fp.close()
    # 发送报告
    send_mail(html_report

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值