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

Python接口自动化测试零基础入门到精通(2025最新版)

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

之前一个项目使用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发生变化,我们只需要修改元素的定位方法,而不需要改动整个框架,相对而言比较快捷。

相关推荐
fzm529811 小时前
C语言单元测试在嵌入式软件开发中的作用及专业工具的应用
自动化测试·单元测试·汽车·嵌入式·白盒测试
测试199817 小时前
一个只能通过压测发现Bug
自动化测试·软件测试·python·selenium·测试工具·bug·压力测试
测试19983 天前
Selenium(Python web测试工具)基本用法详解
自动化测试·软件测试·python·selenium·测试工具·职场和发展·测试用例
测试19984 天前
软件测试方法之边界值分析法
自动化测试·软件测试·python·功能测试·测试工具·职场和发展·测试用例
纳米软件4 天前
电脑电源有哪些测试项目,为什么要测试这些项目呢?-纳米软件
自动化测试·电脑·持续集成系统
程序员杰哥7 天前
python+requests接口自动化测试
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·接口测试
天才测试猿7 天前
自动化测试实践总结
自动化测试·软件测试·python·selenium·测试工具·职场和发展·测试用例
测试19987 天前
功能测试、自动化测试、性能测试的区别
自动化测试·python·功能测试·测试工具·职场和发展·性能测试·安全性测试