• selenium+python自动化测试系列(一):登录


        最近接手商城的项目,针对后台测试,功能比较简单,但是流程比较繁多,涉及到前后台的交叉测试。在对整个项目进行第一轮测试完成之后,考虑以后回归测试任务比较重,为了减轻回归测试的工作量,所以考虑后台可以进行部分自动化测试。

        之前一个项目使用robotframework进行回归测试,了解了python语言,所以这次就使用python+selenium进行自动化回归测试了。

    配置环境

    • python2.7
    • 运行工具pycharm2017

    流程说明

    登录操作步骤说明

    1. 打开登录url,如http://192.168.10.6/login
    2. 在用户名输入框中输入登录的用户名,如test001
    3. 在密码是输入框中输入密码,如testgood001
    4. 点击登录页面的登录按钮,
    5. 登录成功页面,断言登录成功

    线性操作

    根据上面的步骤提示下面代码显示登录操作,如下

    #coding=utf-8
    from selenium import webdriver
    import unittest
    import sys
    reload(sys)
    sys.setdefaultencoding('utf8')
    class TestLogin(unittest.TestCase):
    # 指定浏览器
    	def setUp(self):
    		self.driver = webdriver.Firefox()
    	# 打开url
    		self.driver.get("http://192.168.10.6/login")
    
    	# 登录操作
    	def test_login(self):
    		title = self.driver.title
    		print title
    		now_url = self.driver.current_url
    		print now_url
    		username = "test001"
    		password = "testgood001"
    		# 执行登录操作
    		#用户名的定位
    		self.driver.find_element_by_id("username").clear()
    		self.driver.find_element_by_id("username").send_keys(username)
    		#密码的定位
    		self.driver.find_element_by_id("password").clear()
    		self.driver.find_element_by_id("password").send_keys(password)
    		# 点击登录
    		self.driver.find_element_by_css_selector(".btn.btn-success.btn-block").click()
    		# 登录成功断言
    		login_name = self.driver.find_element_by_xpath('html/body/div[3]/div[2]/ul/li[1]/a/strong').text
    		login_name = login_name.strip('您好:')
    		assert login_name == username
    
    	# 关闭浏览器
    	def tearDown(self):
        		self.driver.quit()
    
    
    if __name__ == "__main__":
    unittest.main()
    

        上面代码显示的是登录成功的正常用例;实际操作中,针对登录不仅仅有正常用例,还有异常用例,如用户名为空,密码为空,用户名错误,密码错误等;我们不能一个用例编写一个py文件,如果这样操作从本质而言相反增加了工作量。

        既然问题出来了,那么如何解决这个问题呢?

        思路:针对登录而言,所有的步骤都是一样的,唯一不同的就是登录的用户名和密码,所以我们可以封装登录步骤,然后只需要专注不同测试用例中的登录的用户名和密码的验证即可。

        这里为了后续测试的简便,使用了selenium中的po模式,即针对每个功能的操作页面进行封装,而后在针对该页面进行测试用例的编写。如这里的登录页面,我们需要针对登录页面进行封装操作,把登录页面中的用户名、密码和登录按钮的定位进行封装,这样用例中只关注输入不同的用户名和密码进行验证即可。

    说明:

        这里浏览器的打开和关闭也进行了封装,放在myunit.py中。

    po模式设计

        整体设计的结构如图所示

        models/driver.py中封装了打开浏览器的操作,这里使用的火狐浏览器进行操作。代码如下

    # -*-coding:utf-8-*-
    # _author_ = "janehost"
    from selenium.webdriver import Remote
    from selenium import webdriver
    import sys
    # 启动浏览器
    reload(sys)
    sys.setdefaultencoding('utf8')
    def browser():
    	driver = webdriver.Firefox()
    	return driver
    
    
    if __name__ == '__main__':
    		dr = browser()
    		dr.get("http://192.168.10.6/login")
    		dr.quit()
    

        models/myunit.py中主要封装了浏览器的启动和关闭的操作,代码如下

    # -*-coding:utf-8-*-
    # _author_ = "janehost"
    import unittest,sys
    from selenium import webdriver
    from driver import browser
    reload(sys)
    sys.setdefaultencoding('utf8')
    
    class MyTest(unittest.TestCase):
    		def setUp(self):
        		self.driver = browser()
        		self.driver.implicitly_wait(10)
        		self.driver.maximize_window()
    
    		def tearDown(self):
        		self.driver.quit()
    
    
    if __name__ == '__main__':
    		unittest.main()
    

        models/function.py中主要封装了截图的操作方法,代码参考如下

    # -*-coding:utf-8-*-
    # _author_ = "janehost"
    from selenium import webdriver
    import os,sys
    reload(sys)
    sys.setdefaultencoding('utf8')
    # 截图函数
    def insert_img(driver, file_name):
    		base_dir = os.path.dirname(os.path.dirname(__file__))
    		base_dir = str(base_dir)
    		base_dir = base_dir.replace('\', '/')
    		base = base_dir.split('test_case')[0]
    		file_path = base + "report/image/" + file_name
    		driver.get_screenshot_as_file(file_path)
    
    
    if __name__ == '__main__':
    		driver = webdriver.Firefox()
    		driver.get("http://192.168.10.6/login")
    		insert_img(driver, 'login.jpg')
    		driver.quit()
    

        下面就是po模式的重点,针对页面的封装,首先创建一个page页面的基本页面,page_objase.py代码如下

    # -*-coding:utf-8-*-
    # _author_ = "janehost"
    
    
    from selenium.webdriver.support.wait import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    import sys
    reload(sys)
    sys.setdefaultencoding('utf8')
    class Page(object):
    
    '''
    页面基础类,用于所有页面的继承
    '''
    
    	base_url = "http://192.168.10.6/login"
    
    	def __init__(self, selenium_driver, base_url=base_url, parent=None):
    	self.base_url = base_url
    	self.driver = selenium_driver
    	self.timeout = 30
    	self.parent = parent
    
    def _open(self, url):
    	url = self.base_url + url
    	self.driver.get(url)
    	assert self.on_page(), 'Did not land on %s' % url
    
    def open(self):
    	self._open(self.url)
    
    def on_page(self):
        #return (self.driver.current_url).encode('utf-8') == (self.base_url + self.url)
    	return self.driver.current_url.encode('utf-8') == (self.base_url + self.url)
    
    def find_element(self, *loc):
        # return self.driver.find_element(*loc)
    	try:
    	# 确保所有元素是可见的
    	# 注意:以下入参为元组的元素,需要加*。python存在这种特性,就是将入参放在元组里。
    	#WebDriverWait(self.driver,10).until(lambda driver: driver.find_element(*loc).is_displayed())
    	# 注意:以下入参本身是元组,不需要加*
    		WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located(loc))
    		return self.driver.find_element(*loc)
    	except:
    		print u"%s 页面中未能找到 %s 元素"%(self, loc)
    
    def find_elements(self, *loc):
    	return self.driver.find_elements(*loc)
    
    def script(self, src):
    	return self.driver.execute_script(src)
    
    def swtich_frame(self, loc):
    	return self.driver.swith_to_frame(loc)
    
    def send_keys(self, loc, value, clear_first=True, click_first=True):
    	try:
    	# getattr相当于self.loc
    		loc = getattr(self, "_%s" % loc)
    		if click_first:
    			self.find_element(*loc).click()
    		if clear_first:
    			self.find_element(*loc).clear()
    		self.find_element(*loc).send_keys(value)
    	except ArithmeticError:
    		print u"%s 页面中未能找到 %s 元素" % (self, loc)
    

        登录页面元素的封装page_objloginPage.py,代码如下

    # -*-coding:utf-8-*-
    # _author_ = "janehost"
    """
    思路:创建登录页面对象,对用户登录页面上的用户名/密码输入框、登录按钮和
    提示信息等元素的定位进行封装。
    """
    from selenium.webdriver.common.action_chains import ActionChains
    from selenium.webdriver.common.by import By
    from base import Page
    from time import sleep
    
    class login(Page):
    
    	'''
    	用户登录界面
    	'''
    	url = '/'
    	# 登录用户名的定位
    	login_username_loc = (By.ID, 'username')
    	# 登录密码的定位
    	login_password_loc = (By.ID,'password')
    	# 登录按钮的定位
    	login_button_loc = (By.CSS_SELECTOR,'.btn.btn-success.btn-block')
    	# 登录错误提示的定位
    	login_error_loc = (By.ID,'error_msg')
    	# 登录成功用户名信息
    	login_user_success_loc = (By.XPATH, 'html/body/div[3]/div[2]/ul/li[1]/a/strong')
    
    	# 登录用户名
    	def login_username(self, username):
    		self.find_element(*self.login_username_loc).clear()
    		self.find_element(*self.login_username_loc).send_keys(username)
    
    	# 登录密码
    	def login_password(self, password):
    		self.find_element(*self.login_password_loc).clear()
    		self.find_element(*self.login_password_loc).send_keys(password)
    
    	# 登录按钮
    	def login_button(self):
    	self.find_element(*self.login_button_loc).click()
    
    	# 统一登录入口
    	def user_login(self, username="testuser01", password="testgood001"):
    	# 获取用户名和页面登录
    		self.open()
    		self.login_username(username)
    		self.login_password(password)
    		self.login_button()
    		sleep(3)
    
    	# 登录错误提示信息
    	def login_error_hint(self):
    		return self.find_element(*self.login_error_loc).text
    
    	# 登录成功用户名信息
    	def login_user_success(self):
    		#return self.find_element(*self.login_user_success_loc).text
    		username = self.find_element(*self.login_user_success_loc).text
    		username = username.strip('您好:')
    		return username
    

        登录测试用例信息test_caselogin_sta.py,代码如下

    # -*-coding:utf-8-*-
    # _author_ = "janehost"
    from time import sleep
    import unittest, random, sys
    from models import myunit, function
    from page_obj.loginPage import login
    sys.path.append("./models")
    sys.path.append("./page_obj")
    reload(sys)
    sys.setdefaultencoding('utf8')
    class loginTest(myunit.MyTest):
    
    	'''
    	测试用户登录
    	'''
    
    	def user_login_verify(self, username="", password=""):
    		login(self.driver).user_login(username, password)
    
    	def test_login1(self):
    	'''用户名、密码为空登录'''
    		self.user_login_verify()
    		po = login(self.driver)
    		self.assertEqual(po.login_error_hint(), '用户名或密码不能为空')
    		function.insert_img(self.driver, "user_pawd_empty.jpg")
    
    	def test_login2(self):
    	'''用户名正确,密码为空登录验证'''
    		self.user_login_verify(username="ces")
    		po = login(self.driver)
    		self.assertEqual(po.login_error_hint(), "用户名或密码不能为空")
    		function.insert_img(self.driver,"pawd_empty.jpg")
    
    	def test_login3(self):
    	'''用户名为空,密码正确'''
    		self.user_login_verify(password="12334ddf")
    		po = login(self.driver)
    		self.assertEqual(po.login_error_hint(),"用户名或密码不能为空")
    		function.insert_img(self.driver, "user_empty.jpg")
    
    	def test_login4(self):
    	'''用户名和密码不匹配'''
    		character = random.choice('abcdefghijklmnopqrstuvwxyz')
    		username = "sdw" + character
    		self.user_login_verify(username=username, password="2sdfd")
    		po = login(self.driver)
    		self.assertEqual(po.login_error_hint(), "用户名或密码错误")
    		function.insert_img(self.driver, "user_pass_error.jpg")
    
    	def test_login5(self):
    	'''用户名、密码正确'''
    		self.user_login_verify(username="adtest" , password="4dscsdx")
    		sleep(3)
    		po = login(self.driver)
    		self.assertEqual(po.login_user_success(), u'adtest')
    		function.insert_img(self.driver, "user_pwd_true.jpg")
    
    
    if __name__ == '__main__':
    	unittest.main()
    

        这样登录的测试用例就完成了。使用po模式之后,如果页面ui发生变化,我们只需要修改元素的定位方法,而不需要改动整个框架,相对而言比较快捷。

    参考:

    小小的博客 [http://www.cnblogs.com/xiaozhiblog/p/5378723.html]

    selenium2 python自动化测试实战(虫师)

  • 相关阅读:
    关于DataGridView的数据源绑定字符串两个值得注意的问题
    .Net 第三方控件(转)
    sql语句linq语言lambda表达式对照
    InvokeRequired 属性 与Invoke方法
    .net垃圾回收机制
    const与readonly的区别
    Developer Express控件组合中的GridControl控件,如何自动显示每一行的序号
    XtraGrid控件6——2种GridColumn的属性
    如何让应用程序仅运行一个实例(c#)
    DevExpress控件之GridControl控件
  • 原文地址:https://www.cnblogs.com/LOVEYU/p/8392269.html
Copyright © 2020-2023  润新知