很多初学者在进行自动化测试过程中,前期都会把测试脚本和元素写在一起。例如,针对一个测试项目,可以多次在页面上模拟用户单击和输入等操作。当开发者修改了代码后,元素位置发生改变,需要重新修改元素定位的语法。当在编写了大量测试用例的情况下,这种维护成本就变得越来越高。本节通过自动化测试脚本的封装实战来解决这一类问题的出现。
以登录功能为例,一般登录的整个流程是不会变化的。例如,输入用户名和密码,单击“登录”按钮。但是对于用户名和密码,“登录”按钮对应的元素可能会出现变化。一种比较简单的方法是将登录流程的脚本封装定义为一个模板,当调用时,只需要对模板进行入参即可。先来看一个简单的线性脚本,示例如下:
from selenium import webdriver from selenium.webdriver.common.by import By # 导入By类 from selenium.webdriver.support.ui import WebDriverWait # 导入WebDriverWait类 from selenium.webdriver.support import expected_conditions as EC # 导入EC模块 import time driver = webdriver.Chrome() # 获取驱动 driver.get('https://www.gjfax.com/toLogin') # 登录测试项目 driver.refresh() driver.implicitly_wait(30) driver.find_element_by_id('mobilePhone').send_keys('18513600×××') # 输入用户名 element = WebDriverWait(driver,10,0.5).until(EC.presence_of_element_located((By.ID, 'password'))) element.send_keys('a123456') # 显示等待输入密码 login_Btn = driver.find_element_by_id('loginBtn').click() # 单击“登录”按钮 time.sleep(3) driver.quit()
上面的测试代码是一个完整的登录流程。在测试代码中,不难发现测试数据、元素和操作流程都是放在一起的,这种方式非常不利于后期脚本的维护。对上面代码进行二次改造,示例如下:
from selenium import webdriver from selenium.webdriver.common.by import By # 导入By类 from selenium.webdriver.support.ui import WebDriverWait # 导入WebDriverWait类 from selenium.webdriver.support import expected_conditions as EC # 导入EC模块 import time def Element_Locator(*new_loctor): '''重写find_element定位方法''' return driver.find_element(*new_loctor) def input_username(username,*userLoctor): '''输入用户名''' return Element_Locator(*userLoctor).send_keys(username) def input_password(password,*passwdLoctor): '''输入密码''' return Element_Locator(*passwdLoctor).send_keys(password) def click_btn(*clickLoctor): '''单击“登录”按钮''' return Element_Locator(*clickLoctor).click() def assert_Login_text(*assertText): '''获取登录成功后的验证信息''' return Element_Locator(*assertText).text
上述代码对By类提供的find_element()方法进行二次封装,封装后的定位统一写法改为Element_Locator(),*idLoctor表示接收的参数是不固定的,注意,所有带*的默认参数在传递时要放在正常参数的后面。将输入用户名、输入密码、单击“登录”按钮和获取登录成功后的验证信息分别封装成了input_username()、input_password()、click_btn()和assert_Login_text()方法,示例如下:
if __name__ == '__main__': # 定位器 user_loc = (By.ID,'mobilePhone') passwd_loc = (By.ID,'password') click_loc = (By.ID,'loginBtn') successLogin_loc = (By.CSS_SELECTOR,'a.fc-blue.mr-5') driver = webdriver.Chrome() # 获取驱动 driver.get('https://www.gjfax.com/toLogin') # 获取测试网址 driver.maximize_window() # 最大化窗口 time.sleep(3) driver.refresh() input_username('18513600×××',*user_loc) # 输入用户名 input_password('a123456',*passwd_loc) # 输入密码 click_btn(*click_loc) # 单击“登录”按钮 time.sleep(3) # 断言 if assert_Login_text(*successLogin_loc) == '安全退出': print('测试用例通过!') else: print('测试用例失败!') driver.quit() dr.quit() # 关闭
在测试用例中调用时,分别传入对应的元素和数据,最后对登录后的结果进行判断。
针对2.14.1小节的代码,可进行代码重构,即把整个登录功能结合面向对象进行再次封装,示例如下:
from selenium import webdriver from time import sleep from selenium.webdriver.common.by import By # 二次定位的By类 class GJsProject(object): def __init__(self): self.driver = webdriver.Chrome() # 定义驱动 def openbrowser(self,url): # 定义基础操作 self.driver.get(url) self.driver.maximize_window() sleep(2) def by_css(self,loc): '''重写css定位''' return self.driver.find_element(By.CSS_SELECTOR,loc) def click_login_btn(self,loc): # 单击“登录”按钮 self.by_css(loc).click() def input_username_Text(self,loc,text): # 输入用户名 self.by_css(loc).send_keys(text) def input_password_Text(self,loc,text): # 输入密码 self.by_css(loc).send_keys(text) def click_login_button(self,loc): # 单击“登录”按钮 self.by_css(loc).click() def assert_success_text(self,loc): # 获取验证信息 return self.by_css(loc).text def logsys_gjs_action(self,loc): # 退出系统 self.by_css(loc).click() sleep(2) self.driver.quit()
openbrowser()方法用于获取地址、最大化窗口基础操作。对By类中的css定位重写为by_css()方法。将输入用户名、输入密码、单击“登录”按钮3个流程封装成单独的方法。assert_success_text()方法用于登录成功后返回文本信息,示例如下:
# 登录流程 def login_gjs(self,url,loc1,loc2,username,loc3,password,loc4,loc5,exceptText,loc6): self.openbrowser(url) sleep(1) self.click_login_btn(loc1) sleep(1) self.input_username_Text(loc2,username) sleep(1) self.input_password_Text(loc3,password) sleep(1) self.click_login_button(loc4) sleep(1) if self.assert_success_text(loc5) == exceptText: # 断言登录是否成功 print('pass') else: print('fail') self.logsys_gjs_action(loc6)
将获取地址、最大化窗口、输入用户名、输入密码和获取验证信息等操作进一步封装为login_gjs()方法,这样做的好处是在调用时只需传入对应的元素和测试数据即可,示例如下:
if __name__ == '__main__': t = GJs() url = 'https://www.gjfax.com/' # 打开项目地址 loc1 = "span.menubar-btn .fc-white" # 定位器 loc2 = "# mobilePhone" username = '18513600×××' loc3 = "# password" password = 'a123456' loc4 = "# loginBtn" loc5 = "a.fc-blue.mr-5" exceptText = '安全退出' loc6 = "a.fc-blue.mr-5" # 调用登录方法 t.login_gjs(url,loc1,loc2,username,loc3,password,loc4,loc5,exceptText,loc6)