51Testing软件测试论坛

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

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 1656|回复: 1
打印 上一主题 下一主题

基于Selenium的web自动化框架

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2018-4-9 10:54:52 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
1 什么是selenium
Selenium 是一个基于浏览器的自动化工具,它提供了一种跨平台、跨浏览器的端到端的web自动化解决方案。
Selenium主要包括三部分:Selenium IDE、Selenium WebDriver 和Selenium Grid:

Selenium IDE:Firefox的一个扩展,它可以进行录制回放,并可以把录制的操作以多种语言(例如java,pyt
hon等)的形式导出成测试用例。
Selenium WebDriver:提供Web自动化所需的API,主要用作浏览器控制、页面元素选择和调试。不同的浏览
器需要不同的WebDriver。
Selenium Grid:提供了在不同机器的不同浏览器上运行selenium测试的能力


本文中主要使用python结合Selenium WebDriver库进行自动化测试框架的搭建。

2 自动化测试框架
一个典型的自动化测试框架一般包括用例管理模块、自动化执行控制器、报表生成模块和日志模块等,这些
模块之间不是相互孤立的,而是相辅相成的。



下面来介绍下每个模块的逻辑单元:

用例管理模块
用例管理模块包括用例的添加、修改、删除等操作单元,这些单元也会涉及到用例书写的模式,测试数据的
管理、可复用库等



自动化执行控制器
控制器是自动化用例执行的组织模块,主要负责以什么方式去执行用例。比较典型的控制器有用户图形界面
(GUI)和“commandline+文件”两种。



报表生成模块
报表生成模块主要负责执行完用例以后生成报表,报表一般以HTML格式居多,信息主要包括用例的执行情况
及相应的总结报告。另外还可以添加发送邮件功能。



日志模块
日志模块主要用来记录用例的执行情况,以便于更高效的调查用例失败信息及追踪用例执行情况。

3 自动化框架的设计与实现
3.1       需求分析
测试对象是一个典型的后台系统的Web展现平台,基于此平台设计的自动化框架要包含测试用例管理、测试
执行控制、测试报表及测试日志的生成,整体测试框架要轻量易用。

3.2       概要设计
概要设计包括了四个大的模块:公共库模块(可复用函数、日志管理、报表管理以及发送邮件管理)、用例
仓库(具体用例的管理)、页面管理(单独对Web页面进行抽象,封装页面元素和操作方法)以及执行模块。

概要设计类图:



3.3       详细设计与实现


3.3.1        页面管理

                测试Web对象是一个典型的单页面应用,因此采用页面模式(page pattern)来进行组织:



页面模式是页面与测试用例之间的桥梁,它将每个页面抽象成一个单独的页面类,为测试用例提供页面元素
的定位和操作。

页面模式的类图如下:



BasePage作为基类只包含一个driver成员变量,它用来标记Selenium中的WebDriver,以便在BasePage的派生
类中定位页面元素。LoginPage和PageN等作为派生类,可以提供相应页面元素的定位和操作方法。比如测试
对象的登录页面:



从页面可以看出,需要操作的页面元素分别为:Username,Password,remember my username checkbox
和Sign in按钮,它们对应的操作为输入用户名和密码,点选checkbox和点击Sign In按钮,具体代码级别的实
现如下:

页面基类BasePage.py:

class BasePage(object):  
    """description of class"""  

    #webdriver instance  
    def __init__(self, driver):  
        self.driver = driver  


LoginPage页面继承自BasePage,并进行Login Page的元素定位及操作实现。代码中定位了username和passwo
rd,并且添加了设置用户名和密码的操作。

复制代码
  1. from BasePage import BasePage  
  2. from selenium.webdriver.common.by import By  
  3. from selenium.webdriver.common.keys import Keys  
  4.   
  5. class LoginPage(BasePage):  
  6.     """description of class"""  
  7.      #page element identifier  
  8.     usename = (By.ID,'username')  
  9.     password = (By.ID, 'password')  
  10.     dialogTitle = (By.XPATH,"//h3[@class=\"modal-title ng-binding\"]")  
  11.     cancelButton = (By.XPATH,'//button[@class=\"btn btn-warning ng-binding\"][@ng-click=\"cancel()\"]')  
  12.     okButton = (By.XPATH,'//button[@class=\"btn btn-primary ng-binding\"][@ng-click=\"ok()\"]')  
  13.   
  14.     #Get username textbox and input username  
  15.     def set_username(self,username):  
  16.         name = self.driver.find_element(*LoginPage.usename)  
  17.         name.send_keys(username)  
  18.       
  19.     #Get password textbox and input password, then hit return  
  20.     def set_password(self, password):  
  21.         pwd = self.driver.find_element(*LoginPage.password)  
  22.         pwd.send_keys(password + Keys.RETURN)  
  23.   
  24.     #Get pop up dialog title  
  25.     def get_DiaglogTitle(self):  
  26.         digTitle = self.driver.find_element(*LoginPage.dialogTitle)  
  27.         return digTitle.text  
  28.   
  29.     #Get "cancel" button and then click  
  30.     def click_cancel(self):  
  31.         cancelbtn = self.driver.find_element(*LoginPage.cancelButton)  
  32.         cancelbtn.click()  
  33.   
  34.      #click Sign in  
  35.     def click_SignIn(self):  
  36.         okbtn = self.driver.find_element(*LoginPage.okButton)  
  37.         okbtn.click()  
复制代码

复制代码




采用页面模式来管理页面和测试用例有很多好处,主要体现在:

简单并且清晰
每个页面都有单独的类来封装页面元素和操作,让页面操作更加具体化,而不是相对独立的。

比如未使用页面模式,测试用例的输入用户名和密码的代码:

  1. #enter username and password  
  2. driver.find_element_by_id("username").clear()  
  3. driver.find_element_by_id("username").send_keys("sbxadmin")  
  4. driver.find_element_by_id("password").clear()  
  5. driver.find_element_by_id("password").send_keys("password"+Keys.RETURN)
复制代码


使用页面模式之后,输入用户名和密码的代码:

#Step2: Open Login page  
login_page = BasePage.LoginPage(self.driver)  
#Step3: Enter username  
login_page.set_username("username")  
#Step4: Enter password  
login_page.set_password("password")  


通过对比我们不难发现,未使用页面模式的代码组织比较混乱,步骤多,可读性非常差,不难想象,一个通篇
都是find_element_by_id或者send_Keys的测试用例到底有多糟糕!而使用了页面模式之后,在哪个页面做什
么操作都非常清晰,非常接近测试用例的步骤,易读性非常好。



可复用性好
由于页面操作都被封装在了页面类中,所以页面方法和容易调用,可复用性非常好。而未使用页面模式的用
例只能每次都实现一遍。




本帖子中包含更多资源

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

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

使用道具 举报

该用户从未签到

2#
 楼主| 发表于 2018-4-9 10:55:07 | 只看该作者


可维护性好
由于测试目标页面的多变性,页面元素的定位经常需要改变,利用了页面模式后,只需要修改一遍其页面类
中的定位就可以对所用用到该元素的测试用例生效;而在未使用该模式的情况下,必须修改每一个用到该元
素的测试用例,非常容易遗漏,工作量也非常大。

综合以上页面模式的各种优点,我们在以后的web自动化中可以多使用该模式来组织页面。

3.3.2        公共库模块

                公共库模块是为创建测试用例服务的,它主要包括常量、公共函数、日志管理、报表管理以及发
送邮件管理等。

                公共库模块涉及到的功能一般多而杂,在设计的时候只要遵循高内聚低耦合就可以了。比如常量、
变量和一些公共函数可以放在同一个文件中Common.py:

复制代码
  1. from datetime import datetime

  2. def driverPath():
  3. return r'C:\Users\xua\Downloads\chromedriver_win32\chromedriver.exe'
  4. def baseUrl():
  5. return "https://xxx.xxx.xxx.xxx:9000"
  6. #change time to str
  7. def getCurrentTime():
  8. format = "%a %b %d %H:%M:%S %Y"
  9. return datetime.now().strftime(format)
  10. # Get time diff
  11. def timeDiff(starttime,endtime):
  12. format = "%a %b %d %H:%M:%S %Y"
  13. return datetime.strptime(endtime,format) - datetime.strptime(starttime,format)
复制代码

复制代码


测试用例信息类用来标识测试用例,并且包括执行用例执行结果信息,主要包括以下字段:

复制代码
  1. class TestCaseInfo(object):
  2. """description of class"""
  3. def __init__(self, id="",name="",owner="",result="Failed",starttime="",endtime="",secondsDuration="",e
  4. rrorinfo=""):
  5. self.id = id
  6. self.name = name
  7. self.owner = owner
  8. self.result = result
  9. self.starttime = starttime
  10. self.endtime = endtime
  11. self.secondsDuration = secondsDuration
  12. self.errorinfo = errorinfo
复制代码

复制代码


测试用例信息需要在每个测试用例中实例化,以便对测试用例进行标记,并最终体现在测试报告中。

日志主要用来记录测试用例执行步骤及产生的错误信息,不同的信息有不同的日志级别,比如Information,
Warning,Critical和Debug。由于每个测试用例产生的日志条目比较少,所以在测试框架中只利用了最高级
别的日志打印,即Debug级别,该级别也会将其他所有的日志级别的信息同样打印出来。在具体的实现中
引用了Python标准库中的logging类库,以便更方便的控制日志输出:

复制代码
  1. import logging
  2. import ResultFolder

  3. logger = logging.getLogger()
  4. logger.setLevel(logging.DEBUG)


  5. def CreateLoggerFile(filename):
  6. try:
  7. fulllogname = ResultFolder.GetRunDirectory()+"\\"+filename+".log"
  8. fh = logging.FileHandler(fulllogname)
  9. fh.setLevel(logging.DEBUG)
  10. formatter = logging.Formatter('%(asctime)s [line:%(lineno)d] %(message)s')
  11. fh.setFormatter(formatter)
  12. logger.addHandler(fh)
  13. except Exception as err:
  14. logger.debug("Error when creating log file, error message: {}".format(str(err)))


  15. def Log(message):
  16. logger.debug(message)
复制代码

复制代码




报表管理及发送邮件模块实现了报表(html格式)的生成及自动发送邮件的功能。报表和邮件依附于当前
测试的执行,每次执行都会独立的触发报表生成和邮件发送。该模块主要运用了Python中的lxml、smtplib
和email库。

3.3.3        用例仓库

                用例仓库主要用来组织自动化测试用例。每条测试用例都被抽象成一个独立的类,并且均继承
自unittest.TestCase类。 Python中的unittest库提供了丰富的测试框架支持,包括测试用例的setUp和tearD
own方法,在实现用例的过程中可以重写。依托页面管理和公共库模块实现的页面方法和公共函数,每一
个测试用例脚本的书写都会非常清晰简洁,一个简单的Floor Manager Lite的登录用例如下:  

复制代码
  1. class Test_TC_Login(unittest.TestCase):
  2. def setUp(self):
  3. self.driver = webdriver.Chrome(cc.driverPath())
  4. self.base_url = cc.baseUrl()
  5. self.testCaseInfo = TestCaseInfo(id=1,name="Test case name",owner='xua')
  6. self.testResult = TestReport()
  7. LogUtility.CreateLoggerFile("Test_TC_Login")
  8. def test_A(self):
  9. try:
  10. self.testCaseInfo.starttime = cc.getCurrentTime()
  11. #Step1: open base site
  12. LogUtility.Log("Open Base site"+self.base_url)
  13. self.driver.get(self.base_url)

  14. #Step2: Open Login page
  15. login_page = LoginPage(self.driver)

  16. #Step3: Enter username & password
  17. LogUtility.Log("Login web using username")
  18. login_page.set_username("username")
  19. login_page.set_password("password")

  20. time.sleep(2)
  21. #Checkpoint1: Check popup dialog title
  22. LogUtility.Log("Check whether sign in dialog exists or not")
  23. self.assertEqual(login_page.get_DiaglogTitle(),"Sign in")

  24. #time.sleep(3)
  25. #Step4: Cancel dialog
  26. login_page.click_cancel()
  27. self.testCaseInfo.result = "Pass"

  28. except Exception as err:
  29. self.testCaseInfo.errorinfo = str(err)
  30. LogUtility.Log(("Got error: "+str(err)))
  31. finally:
  32. self.testCaseInfo.endtime = cc.getCurrentTime()
  33. self.testCaseInfo.secondsDuration = cc.timeDiff(self.testCaseInfo.starttime,self.testCaseInfo.endtime)
  34. def tearDown(self):
  35. self.driver.close()
  36. self.testResult.WriteHTML(self.testCaseInfo)

  37. if __name__ == '__main__':
  38. unittest.main()
复制代码

复制代码


从这个测试用例中,我们可以看到

Setup中定义了执行测试用例前的一些实例化工作
tearDown对执行完测试做了清理和写日志文件工作
测试步骤、测试数据和测试检查点非常清晰,易修改(比如用户名密码)
日志级别仅有Debug,所以写日志仅需用同一Log方法
3.3.4        用例执行模块(控制器)

                执行模块主要用来控制测试用例脚本的批量执行,形成一个测试集。用例的执行引用了Python标
准库中的subprocess来执行nosetests的shell命令,从而执行给定测试用例集中的用例。测试用例集是一个简
单的纯文本文件,实现过程中利用了.txt文件testcases.txt:

  1. Test_Login_pass.py
  2. Test_Login_Fail.py
  3. #Test_MainPage_CheckSecurityTableInfo.py
  4. Test_MainPage_EditSecurityInfo.py
复制代码



用例前没有“#“标记的测试用例脚本会被执行,而有”#“标记的则会被忽略,这样可以很方便的控制测试集
的执行,当然也可以创建不同的文件来执行不同的测试集。

具体的调用代码如下:

复制代码
  1. def LoadAndRunTestCases(self):
  2. try:
  3. f = open(self.testcaselistfile)
  4. testfiles = [test for test in f.readlines() if not test.startswith("#")]
  5. f.close()
  6. for item in testfiles:
  7. subprocess.call("nosetests "+str(item).replace("\\n",""),shell = True)
  8. except Exception as err:
  9. LogUtility.logger.debug("Failed running test cases, error message: {}".format(str(err)))
  10. finally:
  11. EmailUtils.send_report()
复制代码

复制代码


3.4       执行结果
测试用例执行完毕后主要有两种输出:日志和测试报告。测试报告会html附件的形式通过邮件发出,例如:



4 需要改进的模块
     对于现有实现的测试框架,已经可以满足web对象的自动化需求,但还是有些可以改进提高的地方,比如:

针对部分测试用例是否可以尝试数据驱动
添加屏幕截图功能
封装selenium中By库中的函数,以便更高效的定位页面元素等
结合业界优秀的自动化框架和实践持续改进


5 总结
         基于selenium实现的web自动化框架不仅轻量级而且灵活,可以快速的开发自动化测试用例。结合本
篇中的框架设计以及一些好的实践,希望对大家以后的web自动化框架的设计和实现有所帮助。

回复 支持 反对

使用道具 举报

本版积分规则

关闭

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

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

GMT+8, 2024-11-23 12:27 , Processed in 0.067209 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2024 Comsenz Inc.

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