51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 1920|回复: 2
打印 上一主题 下一主题

Selenium自动化测试-unittest单元测试框架使用

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2018-4-4 17:01:39 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
一、什么是unittest

这里我们将要用的unittest是python的单元测试框架,它的官网是
https://docs.python.org/2/library/unittest.html,在这里我们可以得到全面的信息。

当我们写的用例越来越多时,我们就需要考虑用例编写的规范与组织,以便于后期的维护,而unittest正是这
样一款工具。我们这里用一个示例来展示用unittest脚本是什么样子的。借助Selenium IDE的录制功能,可以
完成这样的操作。录制完成后,我们按照如下步骤,将其导出。


然后执行导出的py文件,可以得到类似下面的信息:

---------------------------------------------------
Ran 1 test in 12.801s

OK

我们打开导出的脚本看一看,录制的是在百度里面搜索关键字Selenium2:

  1. # -*- coding: utf-8 -*-
  2. from selenium import webdriver
  3. from selenium.webdriver.common.by import By
  4. from selenium.webdriver.common.keys import Keys
  5. from selenium.webdriver.support.ui import Select
  6. from selenium.common.exceptions import NoSuchElementException
  7. from selenium.common.exceptions import NoAlertPresentException
  8. #导入unittest包
  9. import unittest, time, re

  10. #SearchTest类继承自unittest.TestCase,表明这是一个测试案例
  11. class SearchTest(unittest.TestCase):
  12.     #setUp用于初始化工作
  13.     def setUp(self):
  14.         self.driver = webdriver.Firefox()
  15.         self.driver.implicitly_wait(30)
  16.         self.base_url = "https://www.baidu.com/"
  17.         self.verificationErrors = []
  18.         self.accept_next_alert = True

  19.     #以test开头的是我们的测试脚本   
  20.     def test_search(self):
  21.         driver = self.driver
  22.         driver.get(self.base_url + "/")
  23.         driver.find_element_by_id("kw").click()
  24.         driver.find_element_by_id("kw").clear()
  25.         driver.find_element_by_id("kw").send_keys("selenium2")
  26.         driver.find_element_by_id("su").click()

  27.     def is_element_present(self, how, what):
  28.         try: self.driver.find_element(by=how, value=what)
  29.         except NoSuchElementException as e: return False
  30.         return True

  31.     def is_alert_present(self):
  32.         try: self.driver.switch_to_alert()
  33.         except NoAlertPresentException as e: return False
  34.         return True

  35.     def close_alert_and_get_its_text(self):
  36.         try:
  37.             alert = self.driver.switch_to_alert()
  38.             alert_text = alert.text
  39.             if self.accept_next_alert:
  40.                 alert.accept()
  41.             else:
  42.                 alert.dismiss()
  43.             return alert_text
  44.         finally: self.accept_next_alert = True

  45.     #在每个测试方法后执行,完成清理工作
  46.     def tearDown(self):
  47.         self.driver.quit()
  48.         self.assertEqual([], self.verificationErrors)

  49. #整个测试过程集中在unittest的main()模块中,其默认执行以test开头的方法
  50. if __name__ == "__main__":
  51.     unittest.main()
复制代码


通过这个我们大概对unittest有个直观的了解了。unittest.main():使用它可以将一个单元测试模块变为可直接
运行的测试脚本,main()方法使用TestLoader类来搜索所有包含在该模块中以“test”命名开头的测试方法,并
自动执行。执行方法的默认顺序是:根据ASCII码的顺序加载测试用例,数字与字母的顺序为:0-9,A-Z,
a-z。所以以A开头的测试用例方法会优先执行,以a开头会后执行。

二、unittest中的概念

TestCase:
一个Testcase的实例就是一个测试用例,测试用例就是一个完整的测试流程,包括初始化setUp、运行run、
测试后的还原tearDown。unittest.TestCase类,所有测试用例类继承的基本类。此类提供了很多assert方法
用于检查比较,部分如下:



多数方法都可以见其名知其意,使用的门槛很低。

TestSuite:
对一个功能的测试往往需要多测试用例的,可以把多的测试用例集合在一起执行,这就是TestSuite的概念。
常用addTest()方法将一个测试用例添加到测试套件中。

TextTestRunner:
是用来执行测试用例的,其中的run(test)用来执行TestSuite/TestCase。测试的结果会保存在TextTestResult
实例中。

TestFixture:
测试准备前要做的工作和测试执行完后要做的工作.包括setUp()和tearDown()。通过覆盖TestCase的setUp和t
earDown来实现。

知道了这几个主要的概念,我们就可以把上面的脚本中的最后一行unittest.main(),改为以下代码:

    #构造测试套件
    suite = unittest.TestSuite()
    suite.addTest(SearchTest("test_search"))
    #执行测试
    runner = unittest.TextTestRunner()
    runner.run(suite)

执行之后发现和之前用unittest.main()的结果一样。

三、用例组织

这里我们假设,脚本当中有多个TestCase如test_case1,test_case2…,那我们应该怎样去控制它们的执行顺序呢?

执行测试用例方案一:
直接用unittest.main() 执行,这里它搜索所有以test开头的测试用例方法,按照ASCII的顺序执行多个用例。

执行测试用例方案二:
先实例化测试套件,将用例加载进去,再用TextTestRunner去执行用例:

  1. suite=unittest.TestSuite()
  2. suite.addTest(Test('test_case2'))
  3. suite.addTest(Test('test_case1'))
  4. runner=unittest.TextTestRunner()
  5. runner.run(suite)
复制代码

执行的顺序是用例的加载顺序,比如这里是先执行2后执行1。

执行测试用例方案三:
在方案2中,如果我们有成百上千个用例的话,一个一个add进去,是不太现实的,那么我们可以用defaultTest
Loader来加载:

  1.     test_dir = './'
  2.     discover = unittest.defaultTestLoader.discover(test_dir, pattern='*test.py')
  3.     runner = unittest.TextTestRunner()
  4.     runner.run(discover)
复制代码


这里用./指定了当前目录,指定了*test.py文件,对其当中的用例进行执行,顺序和方案一相同。

如果这里指定的目录下面有多个经pattern匹配上的.py文件呢?调用discover方法,首先通过test_dir定义查找
目录,如果文件名满足定义的pattern,那么我们要用for循环来找出所有被筛选出来的用例,并将其循环加到
套件中,主要代码如下:

  1. for test_suite in discover:
  2.         for test_case in test_suite:
  3.             test_unit.addTests(test_case)
复制代码

由上面组织用例的方式我们可以知道,在实际的测试用脚本开发中,我们可以在目录下创建xx.py的文件,
当用例稳定运行后,可以修改成test_xx.py,以便于添加到测试套件中。注意,文件名的匹配规则,我们可
以随便由pattern参数定义。

如果要执行多级目录结构的用例呢?要想被discover读取执行,我们要在目录下加_ init _.py文件

四、一个例子

下面简单的介绍一个用unittest组织的用例结构,先建立D:\Test_Project目录,下面放上test_case和test_r
eport来分别存放用例和报告。

1. 编写测试用例
在test_case下面编写用例,如下简单的示范了在百度上搜索关键字和点击设置的操作:

文件名为:test_baidu.py

  1. # -*- coding: utf-8 -*-
  2. from selenium import webdriver
  3. import unittest, time, re

  4. class MyTest(unittest.TestCase):

  5.     def setUp(self):
  6.         self.driver = webdriver.Firefox()
  7.         self.driver.implicitly_wait(30)
  8.         self.base_url = "https://www.baidu.com"
  9.         self.accept_next_alert = True

  10.     def test_02baidu_search(self):
  11.         u''' 测试百度搜索'''
  12.         driver = self.driver
  13.         driver.get(self.base_url + "/")
  14.         driver.find_element_by_id("kw").click()
  15.         driver.find_element_by_id("kw").clear()
  16.         driver.find_element_by_id("kw").send_keys("selenium-test")
  17.         driver.find_element_by_id("su").click()
  18.         print("test_baidu__test_02baidu_search")

  19.     def test_01baidu_setting(self):
  20.         u''' 测试百度首页设置 '''
  21.         driver = self.driver
  22.         driver.get(self.base_url + "/")
  23.         driver.find_element_by_css_selector("div#u1 a.pf").click()
  24.         driver.find_element_by_class_name("setpref").click()
  25.         driver.find_element_by_css_selector("div#gxszButton>a.prefpanelgo").click()
  26.         driver.switch_to_alert().accept()
  27.         print("test_baidu__test_01baidu_setting")

  28.     def tearDown(self):
  29.         self.driver.close()

  30. #从all_test中调用时,可以不要这个
  31. if __name__ == "__main__":
  32.     unittest.main()
复制代码


为了显示出组织测试用例的效果,我们将此文件再复制一份,把文件名和方法名等修改一下:

文件名为:test_baidu2.py

  1. # -*- coding: utf-8 -*-
  2. from selenium import webdriver
  3. import unittest, time, re

  4. class MyTest(unittest.TestCase):
  5.     u''' 测试baidu的第二个用例'''
  6.     def setUp(self):
  7.         self.driver = webdriver.Firefox()
  8.         self.driver.implicitly_wait(30)
  9.         self.base_url = "https://www.baidu.com"
  10.         self.accept_next_alert = True

  11.     def test_02baidu_search(self):
  12.         u''' 测试baidu的第二个用例的test_02baidu_search'''
  13.         driver = self.driver
  14.         driver.get(self.base_url + "/")
  15.         driver.find_element_by_id("kw").click()
  16.         driver.find_element_by_id("kw").clear()
  17.         driver.find_element_by_id("kw").send_keys("selenium-test")
  18.         driver.find_element_by_id("su").click()
  19.         print("test_baidu2__test_02baidu_search")



复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

x
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏
回复

使用道具 举报

该用户从未签到

2#
 楼主| 发表于 2018-4-4 17:02:17 | 只看该作者
def test_01baidu_setting(self):
u''' 测试baidu的第二个用例的test_01baidu_setting'''
driver = self.driver
driver.get(self.base_url + "/")
driver.find_element_by_css_selector("div#u1 a.pf").click()
driver.find_element_by_class_name("setpref").click() driver.find_element_by_css_selector("div#gxszButton>a.prefpanelgo").click()
driver.switch_to_alert().accept()
print("test_baidu2__test_01baidu_setting")

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

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

2. 美化报告样式和发送结果邮件
上面我们写了 两个测试用例作为示例,我们也可以添加更多的进去。接着我们使用HTMLTestRunner这个开
源模块来美化测试报告,关于它的下载使用可以参考https://pypi.python.org/pypi/HTMLTestRunner。然后,
我们可以在代码中写上运行完成之后自动发送测试邮件出来,便于我们查看。请参看以下代码:

#coding=utf-8
import unittest
import smtplib
from email.mime.text import MIMEText
from email.header import Header
import time
import HTMLTestRunner
from email.mime.application import MIMEApplication

#---发送邮件---
def send_email(report_file):
sender = "XXXXXX@qq.com"
receiver = "XXXXXX@qq.com"
smtpserver = "smtp.qq.com"
#发送邮箱的账号密码,此处使用的是qq邮箱和第三方登录的授权码
username = "XXXXXX@qq.com"
password = "gfomcomojtuudijc"

#定义邮件正文
file = open(report_file,"rb")
mail_body = file.read()
file.close()

msg = MIMEText(mail_body, _subtype="html", _charset="utf-8")
msg["Subject"] = u"自动化测试报告"

smtp = smtplib.SMTP_SSL("smtp.qq.com")
smtp.login(username, password)
smtp.sendmail(sender, receiver, msg.as_string())
smtp.quit()
print("Email has send out !")

#---将用例添加到测试套件---
def creatsuite():
testunit=unittest.TestSuite()
test_dir = "D:\\Test_Project\\test_case"
discover = unittest.defaultTestLoader.discover(test_dir, pattern="test*.py",
top_level_dir = None)
for test_suite in discover:
for test_case in test_suite:
testunit.addTest(test_case)
print (testunit)
return testunit

if __name__ == "__main__":
current_time = time.strftime("%Y-%m-%d-%H-%M")
report_dir = "D:\\Test_Project\\test_report\\"
report_file = report_dir + current_time + "-Test_Result.html"
report_stream = open(report_file, "wb")
# runner = unittest.TextTestRunner()
# 注意HTMLTestRunner只支持python2
runner = HTMLTestRunner.HTMLTestRunner(stream=report_stream,title=u"自动化测试报告", description=u"用例执行情况如下:")
runner.run(creatsuite())
report_stream.close()
send_email(report_file)

在上面的代码中我们使用了runner = HTMLTestRunner.HTMLTestRunner()方法来代替runner = unittest.Tex
tTestRunner(),是为了使用HTMLTestRunner这个模块来美化和输出美观的报告。然后调用方法来发送邮件。
运行此文件后,可以得到以下输出的报告:


可以看见使用这个可以清晰的看到用例的执行情况,也便于查看失败用例的原因去调试它。
同时,在我们输入的收件箱里也会受到一份通知邮件,我们可以将此输出报告添加到邮件的正文或附件中,
以便于查看。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

x
回复 支持 反对

使用道具 举报

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

GMT+8, 2024-11-17 22:18 , Processed in 0.068825 second(s), 24 queries .

Powered by Discuz! X3.2

© 2001-2024 Comsenz Inc.

快速回复 返回顶部 返回列表