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

    之前一个项目使用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_obj\base.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_obj\loginPage.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_case\login_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自动化测试实战(虫师)

selenium+python自动化测试系列(一):登录的更多相关文章

  1. selenium+python自动化测试系列(二):AutoIt工具实现本地文件上传

    AutoIt使用简单说明 AutoIt的安装这里就不在啰嗦,可以参考AutoIt安装或者自行搜索解决. 第一步:定位上传文件路径的文本框 这里举例说明,如何定位?如图 这里我们看到上传文件的类型是bu ...

  2. selenium+python自动化测试系列---基础知识篇(1、HTML基础知识1)

    1.什么是HTML HTML是一种描述网页的语言.HTML指超文本标记语言(Hyper Text Markup Language),它不是一种编程语言,而是一种标记语言(markup language ...

  3. selenium + python自动化测试unittest框架学习(二)

    1.unittest单元测试框架文件结构 unittest是python单元测试框架之一,unittest测试框架的主要文件结构: File >report >all_case.py &g ...

  4. selenium + python 自动化测试环境搭建

    selenium + python 自动化测试 —— 环境搭建 关于 selenium Selenium 是一个用于Web应用程序测试的工具.Selenium测试直接运行在浏览器中,就像真正的用户在操 ...

  5. selenium+python自动化测试

    F12: 右键   选择复制  path 在selenium+python自动化测试(一)–环境搭建中,运行了一个测试脚本,脚本内容如下: from selenium import webdriver ...

  6. selenium + python自动化测试unittest框架学习(五)webdriver的二次封装

    因为webdriver的api方法很长,再加上大多数的定位方式是以xpath方式定位,更加让代码看起来超级长,为了使整体的代码看起来整洁,对webdriver进行封装,学习资料来源于虫师的<se ...

  7. selenium + python自动化测试环境搭建

    selenium的在python平台的搭建: 搭建平台windows 准备工具如下: --------------------------------------------------------- ...

  8. selenium + python自动化测试环境搭建--亲测

    环境准备: 1.下载所学安装包: setuptools https://pypi.python.org/packages/2.7/s/setuptools/ selenium https://pypi ...

  9. selenium + python自动化测试unittest框架学习(一)selenium原理及应用

    unittest框架的学习得益于虫师的<selenium+python自动化实践>这一书,该书讲得很详细,大家可以去看下,我也只学到一点点用于工作中,闲暇时记录下自己所学才能更加印象深刻. ...

随机推荐

  1. TurnipBit:DIY音乐盒教程实例

    一款可以自己DIY的音乐盒,要什么曲子,就自己谱曲啦!为他(她)制作一首他喜欢的音乐,来代表您的心意,也可以让他自己来制作他最爱的音乐哦!更可以带孩子一起体验谱写欢快的音乐. 最近发现一很好玩的中国式 ...

  2. 修改wamp中mysql的默认空密码

    WAMP安装好后,mysql密码是为空的,那么要如何修改呢? 一.打开mysql控制台. 提示输入密码,因为现在是空,所以直接按回车. 二.输入"use mysql",意思是使用m ...

  3. Specified key was too long max key length is 1000 bytes

    Mysql建立索引时遇到一个问题就是索引字段长度太长,解决办法: 1.修改字段长度 : 2.修改mysql默认的存储引擎 ,修改为INNODB: https://www.2cto.com/databa ...

  4. CentOS7下安装Docker-Compose

    Docker-Compose是一个部署多个容器的简单但是非常必要的工具. 安装Docker-Compose之前,请先安装 python-pip 安装 python-pip 1.首先检查linux有没有 ...

  5. iOS学习——UIAlertController详解

    在开发中,弹出提示框是必不可少的.这两天项目中统一对已经被iOS API废弃的UIAlertView和UIActionSheet进行替换,我们知道,UIAlertView和UIActionSheet都 ...

  6. 简单单页面路由跳转demo

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. [转]python变量作用域的有趣差别

    func()里 可以访问全局变量i,但不能给i重新赋值. i = 1 def func(): print( i + 1) func() # 2 用global声明后,可以给i重新赋值. i = 1 d ...

  8. bzoj:3730: 震波

    Description 在一片土地上有N个城市,通过N-1条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为1,其中第i个城市的价值为value[i].不幸的是,这片土地常常发生地震,并且随着时 ...

  9. 51Nod 1277 字符串中的最大值(KMP,裸题)

    1277 字符串中的最大值 题目来源: Codility 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 一个字符串的前缀是指包含该字符第一个字母的连续子串,例如: ...

  10. HDU1114Piggy-Bank(完全背包)

    Piggy-Bank Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total ...