[TOC] ## 重构页面类 ### PO(Page Objects)设计模式 PO设计模式,旨在为每个待测网页创建一个对象。通过这样的设计,**将业务逻辑代码与测试代码分离** 。其中业务逻辑都封装在页面类中。 根据这个设计思路,我们可将页面登录过程定义在登录页面类方法中。 在测试代码中,直接引用登录页面类中的登录方法,即可完成登录操作。 编写一个`page.py` 模块,其中定义一个基类与登录页面类 ```python #!/usr/bin/env python # -*- coding: utf-8 -*- from selenium.webdriver.common.by import By from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class BasePage(object): """ 定义所有页面类的基类 """ def __init__(self, driver): self.driver = driver def get_source(self): """ 获取页面源码 :return: """ return self.driver.page_source def accept_alert(self): """ 切换到alert窗口,并且点击确定 :return: 返回alert信息 """ wait = WebDriverWait(self.driver, 10, 0.5) wait.until(EC.alert_is_present()) alert = self.driver.switch_to.alert return alert class LoginPage(BasePage): """ 登录页面类 """ # 定义第一个待测页面 url = "http://a.4399en.com/" def login(self, user, pwd): # 打开首页 self.driver.get(LoginPage.url) # 点击右上角登录按钮打开登录窗口 self.driver.find_element_by_class_name("CNlogin").click() # 输入用户名 wait = WebDriverWait(self.driver, 20, 0.5) account = wait.until(EC.visibility_of_element_located((By.ID, "modify-account"))) account.clear() account.send_keys(user) # 输入密码 password = self.driver.find_element_by_id("modify-password") password.clear() password.send_keys(pwd) # 点击登录按钮 self.driver.find_element_by_xpath("//input[@type='submit'][@value='Login']").click() if __name__ == '__main__': from selenium import webdriver options = webdriver.ChromeOptions() options.add_argument('User-Agent=Mozilla/5.0 (Linux; U; Android 4.0.2; en-us; Galaxy Nexus Build/ICL53F) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30') driver = webdriver.Chrome(chrome_options=options) login_page = LoginPage(driver) login_page.login("gfc@qq.com", "123456") ``` 主要关注`BasePage`和`LoginPage`这两个类的代码。` if __name__ == '__main__'` 中的代码不是非必要的,主要是调试当前页面类的代码,不会在其他模块调用时运行。 ## 重构测试代码 ### 使用pytest测试框架来组织用例 >[warning]如果你还不懂得如何使用pytest测试框架,请先阅读我的另外一本python入门书中的测试框架《[接口测试框架](https://www.kancloud.cn/guanfuchang/python_start/703690)》 下面直接演示我的例子: 首先,定义公共的fixture,新建一个fixture文件`conftest.py`: ```python #!/usr/bin/python #coding=utf-8 from selenium import webdriver import pytest @pytest.fixture() def get_driver(): # 创建Chrome驱动实例,这里创建driver时,传入chrome_options参数,告诉服务器,我是用移动端浏览器访问的。 options = webdriver.ChromeOptions() options.add_argument('User-Agent=Mozilla/5.0 (Linux; U; Android 4.0.2; en-us; Galaxy Nexus Build/ICL53F) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30') driver = webdriver.Chrome(chrome_options=options) # 设置浏览器大小,让它看起来跟手机的样式差不多。 driver.set_window_size("380", "680") # 设置一个全局的等待超时时间 10s driver.implicitly_wait(10) yield driver driver.quit() ``` 我为什么会将fixture抽离出来,放到公共文件中呢?那是因为考虑到一个项目,后续会有很多测试类,会在各个测试类中共用fixture。 下面以前面的登录测试类为例,新建一个测试文件`test_login.py`: ```python #!/usr/bin/python #coding=utf-8 import pytest from page import LoginPage class TestLogin(object): def test_login_succss(self, get_driver): """登录成功用例""" login_page = LoginPage(get_driver) login_page.login("gfc@qq.com","123456") assert "gfc@qq.com" in login_page.get_source() def test_login_fail(self, get_driver): """登录失败用例""" login_page = LoginPage(get_driver) login_page.login("gfc@123.com","123456") alert = login_page.accept_alert() assert "User does not exist!" in alert.text if __name__ == '__main__': pytest.main() ``` 运行效果如 ![](https://box.kancloud.cn/5f2d4e1b0bbe8546be0c58e9fbcb443c_936x772.gif) ## 思考优化点 * 元素定位与页面类分离 * 测试数据与测试代码分离 * 输出优美的测试报告 很多书籍会建议我们将元素定位与页面分离,但是以我初学者的角度,我认为这一步不用那么着急着做,直接定义在页面类中,在开发和调试时,,会更加便捷。 继续关注下一章,测试代码与Allure结合,生成优美的测试报告。 <hr style="margin-top:50px"> <section class="" style="box-sizing: border-box;" powered-by="xiumi.us"><section class="" style="margin: 40px 0% 10px;box-sizing: border-box;"><section class="" style="display: inline-block;width: 100%;border-width: 5px;border-style: double;border-color: rgb(23, 22, 24);padding: 10px;border-radius: 2px;box-sizing: border-box;"><section class="" style="box-sizing: border-box;" powered-by="xiumi.us"><section class="" style="transform: translate3d(20px, 0px, 0px);-webkit-transform: translate3d(20px, 0px, 0px);-moz-transform: translate3d(20px, 0px, 0px);-o-transform: translate3d(20px, 0px, 0px);font-size: 11px;margin: -50px 0% 0px;box-sizing: border-box;"><section class="" style="box-sizing: border-box;width: 7em;height: 7em;display: inline-block;vertical-align: bottom;border-radius: 100%;border-width: 4px;border-style: double;border-color: rgb(23, 22, 24);background-position: center center;background-repeat: no-repeat;background-size: cover;background-image: url(&quot;http://pav7h2emv.bkt.clouddn.com/FnD-fHkNDLN1-b02XmnMvsz6ld-n&quot;);"><section class="" style="width: 100%;height: 100%;overflow: hidden;box-sizing: border-box;"><img class="" data-ratio="0.6012024" data-w="499" data-src="http://pav7h2emv.bkt.clouddn.com/FnD-fHkNDLN1-b02XmnMvsz6ld-n" style="opacity: 0; box-sizing: border-box; width: 100% !important; height: auto !important; visibility: visible !important;" width="100%" data-type="jpeg" _width="100%" src="http://pav7h2emv.bkt.clouddn.com/FnD-fHkNDLN1-b02XmnMvsz6ld-n" data-fail="0"></section></section></section></section><section class="" style="box-sizing: border-box;" powered-by="xiumi.us"><section class="" style="margin: -30px 0% 30px;box-sizing: border-box;"><section class="" style="display: inline-block;vertical-align: top;width: 61.8%;padding: 0px 15px;box-sizing: border-box;"><section class="" style="box-sizing: border-box;" powered-by="xiumi.us"><section class="" style="margin: 40px 0% 0px;box-sizing: border-box;"><section class="" style="color: rgb(160, 160, 160);box-sizing: border-box;"><p style="margin: 0px;padding: 0px;box-sizing: border-box;">微信公众号:</p><p style="margin: 0px;padding: 0px;box-sizing: border-box;">python测试开发圈</p><p style="margin: 0px;padding: 0px;box-sizing: border-box;"><br style="box-sizing: border-box;"></p></section></section></section></section><section class="" style="display: inline-block;vertical-align: top;width: 38.2%;box-sizing: border-box;"><section class="" style="box-sizing: border-box;" powered-by="xiumi.us"><section class="" style="text-align: center;margin: 10px 0% 0px;box-sizing: border-box;"><section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;border-width: 0px;border-radius: 0px;box-shadow: rgb(0, 0, 0) 0px 0px 0px;width: 90%;overflow: hidden !important;box-sizing: border-box;"><img data-ratio="1" data-w="430" data-src="http://pav7h2emv.bkt.clouddn.com/FibGgIJSMfHtehzeWOOzjdQKSMx5" style="vertical-align: middle; max-width: 100%; box-sizing: border-box; width: 100% !important; height: auto !important; visibility: visible !important;" width="100%" data-type="jpeg" _width="100%" class="" src="http://pav7h2emv.bkt.clouddn.com/FibGgIJSMfHtehzeWOOzjdQKSMx5" data-fail="0"></section></section></section></section></section></section><section class="" style="box-sizing: border-box;" powered-by="xiumi.us"><section class="" style="margin: -30px 0% 0px;box-sizing: border-box;"><section class="" style="display: inline-block;vertical-align: top;width: 61.8%;padding: 0px 15px;box-sizing: border-box;"><section class="" style="box-sizing: border-box;" powered-by="xiumi.us"><section class="" style="transform: translate3d(5px, 0px, 0px);-webkit-transform: translate3d(5px, 0px, 0px);-moz-transform: translate3d(5px, 0px, 0px);-o-transform: translate3d(5px, 0px, 0px);box-sizing: border-box;"><section class="" style="color: rgb(160, 160, 160);font-size: 14px;box-sizing: border-box;"><p style="margin: 0px;padding: 0px;box-sizing: border-box;">一起分享学习与成长路线</p></section></section></section></section><section class="" style="display: inline-block;vertical-align: top;width: 38.2%;box-sizing: border-box;"><section class="" style="box-sizing: border-box;" powered-by="xiumi.us"><section class="" style="transform: translate3d(10px, 0px, 0px);-webkit-transform: translate3d(10px, 0px, 0px);-moz-transform: translate3d(10px, 0px, 0px);-o-transform: translate3d(10px, 0px, 0px);box-sizing: border-box;"><section class="" style="color: rgb(160, 160, 160);font-size: 14px;box-sizing: border-box;"><p style="margin: 0px;padding: 0px;box-sizing: border-box;">长按(或扫描)二维码关注</p></section></section></section></section></section></section></section></section></section>