1、UI自动化用例增加前置
1、fixture(夹具)的使用
前置顾名思义是在执行测试用例之前做的一些事情,在自动化测试时会碰到用例执行前需要做一些前置操作,以及用例执行后需要做一些后置操作,比如登录、退出等,通过pytest fixture可以很方便实现。那么根据上篇文章写的用例,写一个获取浏览器驱动的前置,如下:
@pytest.fixture()
def driver_fixture():
driver = webdriver.Chrome()
driver.maximize_window()
yield driver
time.sleep(5)
driver.quit()
将这个前置放在项目目录下的conftest.py文件中,可以实现数据共享,不需要import 导入 conftest.py,pytest用例就会自动查找。对 于一些公共的fixture我们可以将其存放到conftest.py进行管理。
针对上一篇文章,点击登录的操作也是很多用例都要遇到的,那么对这个操作也写一个夹具,如下:
import pytest
from selenium import webdriver
from d9_fixture前置后置处理及日志封装.pageobjects.home_page import HomePage
from d9_fixture前置后置处理及日志封装.pageobjects.login_page import LoginPage
# 自定义的fixture函数 -- 可以共享使用了,会在每个测试函数运行时都会执行?scope='function'
# session代表的是会话,整个测试用例运行就是一次会话
@pytest.fixture()
def get_driver():
print("这是前置")
driver = webdriver.Chrome()
driver.get("http://mall.banabann.com:3344/")
driver.maximize_window()
yield driver
print("这是后置")
driver.quit()
'''
此时,函数的返回值就是driver,因此前置返回就是driver,可以使用夹具替换掉其他文件中的实例化驱动语句
'''
# 登录也是前置,因此,登录也可以作为夹具
@pytest.fixture()
def login(get_driver):
# 点击首页的登录操作
print("这是login的前置")
homepage = HomePage(get_driver)
homepage.click_login_link()
# 在登陆页面进行登录操作
loginpage = LoginPage(get_driver)
loginpage.login("auto", "123456")
yield get_driver
print("这是login的后置")
注意点:login夹具中,调用了get_driver夹具,那么输出语句执行顺序是:
1、get_driver的前置
2、login的前置
3、login的后置
4、get_driver的后置
可以参考下图:
2、fixture的作用域
fixture默认的作用域是function(函数)级别的,每次执行测试函数时都会运行fixture前置/后置,如果 想要fixture在全部测试用例执行期间只运行一次,可以设置为session(会话)级别。举例如下:
@pytest.fixture(scope='session')
def driver_fixture():
driver = webdriver.Chrome()
driver.maximize_window()
yield driver
time.sleep(5)
driver.quit()
2、关于UI自动化用例执行期间,浏览器关闭打开的最优选择
浏览器在执行期间的打开关闭有2种类型:
1、执行所有用例,浏览器只打开/关闭一次(可以通过给夹具设定作用域为session实现)
2、每次执行一条用例,都会有浏览器打开/关闭的操作
那么我们该选择哪一种呢?选择第2种,原因如下:
1、UI自动化不稳定
2、前面一个用例失败,导致后面用例接连失败
3、最好每条用例都可以单独执行-打开/关闭浏览器-可以避免很多问题
2、loguru日志封装
1、日志的级别
DEBUG 调试
INFO 一般的提示信息
WARN 警告
ERROR 程序已经发生了错误
CRITICAL ERROR 严重的错误
2、添加日志的写法
from loguru import logger
logger.add(sink="my.log",
encoding="utf8",
level="INFO",
rotation="1kB",
retention=20)
3、实战
主要改动点:
1、在base_page.py中增加异常捕获代码
2、在base_page.py中二次封装点击、获取文本、输入文本的函数
3、给全部用例执行的轨迹增加日志信息
4、增加了login夹具
目录:
run.py的文件内容:
import pytest
# -s,-v选项可以让pytest执行的日志更加的丰富
pytest.main(['-s','-v'])
conftest.py的内容:
import pytest
from selenium import webdriver
from d9_fixture前置后置处理及日志封装.pageobjects.home_page import HomePage
from d9_fixture前置后置处理及日志封装.pageobjects.login_page import LoginPage
# 自定义的fixture函数 -- 可以共享使用了,会在每个测试函数运行时都会执行?scope='function'
# session代表的是会话,整个测试用例运行就是一次会话
@pytest.fixture()
def get_driver():
print("这是前置")
driver = webdriver.Chrome()
driver.get("http://mall.banbanban.com:3344/")
driver.maximize_window()
yield driver
print("这是后置")
driver.quit()
'''
此时,函数的返回值就是driver,因此前置返回就是driver,可以使用夹具替换掉其他文件中的实例化驱动语句
'''
# 登录也是前置,因此,登录也可以作为夹具
@pytest.fixture()
def login(get_driver):
# 点击首页的登录操作
print("这是login的前置")
homepage = HomePage(get_driver)
homepage.click_login_link()
# 在登陆页面进行登录操作
loginpage = LoginPage(get_driver)
loginpage.login("auto", "123456")
yield get_driver
print("这是login的后置")
base_page.py 文件的内容:
from d9_fixtur.pageobjects.home_page import HomePage
from d9_fixture.pageobjects.my_order_page import MyOrderPage
from d9_fixture.pageobjects.product_detail_page import ProductDetailPage
from d9_fixture.pageobjects.submit_order_page import SubmitOrderPage
def test_submit_order(login):
# 登录,点击首页的登录链接
home_page = HomePage(login)
# 点击商品列表
home_page.click_goods_list()
# 搜索选择商品
home_page.search_select_product("Mac")
# 商品详情页
product_detail_page = ProductDetailPage(login)
product_name = product_detail_page.get_product_name()
product_price = product_detail_page.get_product_price()
product_detail_page.buy_now()
# 提交订单
submit_order_page = SubmitOrderPage(login)
submit_order_page.submit_order()
# 进入我的订单页面
home_page.my_order()
my_order_page = MyOrderPage(login)
# 断言
# 1、检查订单商品名称
assert my_order_page.get_product_name() == product_name
# 2、检查订单商品数量
assert my_order_page.get_product_num() == '1'
# 3、检查订单商品价格
assert my_order_page.get_product_price() == product_price
# 4、检查订单的状态
assert my_order_page.get_order_status() == '待支付'
home_page.py文件的内容:
import time
from selenium.webdriver.common.by import By
from d9_fixtur.common.base_page import BasePage
class HomePage(BasePage):
# 属性-登录链接
login_link_locator = (By.LINK_TEXT,'登录')
# 欢迎提示信息
welcome_tips_locator = (By.XPATH,'//span[text()="欢迎来到我的世界"]')
welcome_tips_text_locator = (By.XPATH,'//span[@class="text"]')
# 用户名
username_text_locator = (By.XPATH,'//a[@class="link-name"]')
# 点击个人中心
person_center_locator = (By.XPATH,"//span[text()='个人中心']")
# 点击商品列表
goods_list_locator = (By.LINK_TEXT,"商品列表")
# 搜索框
serach_input_locator = (By.XPATH,"//input[@class='search-input']")
# 搜索按钮
search_button_locator = (By.XPATH,"//input[@value = '搜索']")
# 搜索第一个商品
first_goods_locator = (By.XPATH,"//div[@class='goods-img']")
# 我的订单定位
my_order_button_locator = (By.XPATH,"//span[@data-route='order']")
def click_login_link(self):
time.sleep(5)
self.click(self.login_link_locator)
def is_display_welcome_tips(self):
time.sleep(1)
return self.wait_element_visible(self.welcome_tips_locator).is_displayed()
def get_username_text(self):
return self.get_text(self.username_text_locator)
# 点击个人中心按钮
def click_person_center(self):
self.click(self.person_center_locator)
# 点击商品列表
def click_goods_list(self):
self.click(self.goods_list_locator)
# 进入我的订单页面
def my_order(self):
time.sleep(2)
self.click(self.my_order_button_locator)
# 搜索并选择对应的商品
def search_select_product(self,goodname):
time.sleep(1)
self.input_text(self.serach_input_locator, goodname)
self.click(self.search_button_locator)
# 选择第一个商品
time.sleep(1)
self.click(self.first_goods_locator)
login.py文件的内容:
import time
from selenium.webdriver.common.by import By
from d9_fixture前置后置处理及日志封装.common.base_page import BasePage
class LoginPage(BasePage):
# 属性->元素定位信息(元素定位方法+元素定位值)-元组类型
phone_input_locator = (By.XPATH, '//input[@placeholder="请输入手机号/用户名"]')
pwd_input_locator = (By.XPATH, '//input[@placeholder="请输入密码"]')
login_button_locator = (By.CLASS_NAME, 'login-button')
login_tips_locator = (By.XPATH,'//p[@class="el-message__content"]')
# 用户名输入框错误提示文本
user_input_error_tips_locator = (By.XPATH, '//div[contains(@class,"msg-error")]/following-sibling::div[1]')
def login(self,phone,pwd):
# 因为继承了 basepage类,所以克不用再写driver参数
# self.wait_element_visible(self.driver,self.phone_input_locator).send_keys("17728373518")
self.input_text(self.phone_input_locator, phone)
self.input_text(self.pwd_input_locator, pwd)
# 点击登录按钮
time.sleep(3)
self.click(self.login_button_locator)
def get_login_tips(self):
# 这里比较文本内容
time.sleep(2)
return self.get_text(self.login_tips_locator)
def get_user_input_error_tips(self):
return self.get_text(self.user_input_error_tips_locator)
my_order_page.py文件的内容:
from time import sleep
from selenium.webdriver.common.by import By
# 我的订单页面
from d9_fixture.common.base_page import BasePage
class MyOrderPage(BasePage):
# 最新的一笔订单的商品名
goods_name_locator = (By.XPATH,"//a[@class='name']")
# 商品数量
goods_num_locator = (By.XPATH,"//div[@class='goods-number']")
# 商品付款总额
pay_price_locator = (By.XPATH,"//td[@rowspan='1']/div/div/span")
# 订单状态
order_status_locator = (By.XPATH,"//div[@class='status']/div")
def get_product_name(self):
return self.get_text(self.goods_name_locator)
def get_product_num(self):
sleep(2)
goods_num = self.get_text(self.goods_num_locator)
return goods_num[1:]
def get_product_price(self):
product_amount = self.get_text(self.pay_price_locator)
return product_amount[1:]
def get_order_status(self):
order_status = self.get_text(self.order_status_locator)
return order_status.strip()
product_detail_page.py文件的内容:
from selenium.webdriver.common.by import By
# 商品详情页面
from d9_fixture.common.base_page import BasePage
class ProductDetailPage(BasePage):
# 商品名
product_name_locator = (By.XPATH, "//div[@class='name-box']/div[@class='name']")
# 商品价格
product_price_locator = (By.XPATH, '//div[@class="price"]')
# 添加购物车
button_addcart_locator = (By.CLASS_NAME, "add-cart")
# 立即购买
button_buynow_locator = (By.CLASS_NAME, "buy-now")
# 收藏商品
button_collect_locator = (By.CLASS_NAME, "collect")
def add_cart(self):
self.click(self.button_addcart_locator)
def buy_now(self):
self.click(self.button_buynow_locator)
def get_product_name(self):
return self.get_text(self.product_name_locator)
def get_product_price(self):
product_price = self.get_text(self.product_price_locator)
# 将换行符号去掉
product_price = product_price.replace('\n', '')
# 去掉开头的'¥' ‘¥0.01’
return product_price[1:]
submit_order_page.py文件的内容:
from selenium.webdriver.common.by import By
# 提交订单页面
from d9_fixture.common.base_page import BasePage
class SubmitOrderPage(BasePage):
# 提交订单按钮
button_submit_locator = (By.LINK_TEXT, "提交订单")
def submit_order(self):
self.click(self.button_submit_locator)
user_center_page.py文件的内容:
from time import sleep
from selenium.webdriver.common.by import By
from d9_fixture.common.base_page import BasePage
class PersonCenterPage(BasePage):
take_delivery_goods_link_locaotr = (By.LINK_TEXT,"收货地址")
add_new_address_locator = (By.LINK_TEXT,"新增收货地址")
recipient_name_locator = (By.XPATH,"//div[text()='收件人:']/following-sibling::div/input")
recipient_phone_locator = (By.XPATH,"//div[@class='item'][1]//input[@class='input']")
recipient_province_locator = (By.XPATH,"//div[@prop='province']//i")
click_prodropdown_icon_locator = (By.XPATH,"//div[@x-placement='bottom-start']//li[text()='陕西省']")
recipient_city_locator = (By.XPATH,"//div[@prop='city']//i")
click_citydropdown_icon_locator = (By.XPATH,"//div[@x-placement='bottom-start']//li[text()='延安市']")
recipient_area_locator = (By.XPATH,"//div[@prop='area']//i")
click_areadropdown_icon_locator = (By.XPATH,"//div[@x-placement='bottom-start']//li[text()='黄龙县']")
detail_address_locator = (By.XPATH,"//div[text()='详细地址:']/parent::div/div[2]/input")
submit_button_locator = (By.LINK_TEXT,"保存收件人信息")
add_address_success_tips_locator = (By.CLASS_NAME,"el-message__content")
# 将新增地址的所有操作都放在一个函数中
def write_detail_address(self,name,phone,province,city,area,detail_address):
# 点击收货地址
self.click(self.take_delivery_goods_link_locaotr)
# 点击新增地址按钮
self.click(self.add_new_address_locator)
# 填写姓名
sleep(2)
self.input_text(self.recipient_name_locator, name)
# 填写电话号码
self.input_text(self.recipient_phone_locator, phone)
# 填写省份信息
self.click(self.recipient_province_locator)
sleep(1)
self.click((By.XPATH,f"//div[@x-placement='bottom-start']//li[text()='{province}']"))
# 填写城市信息
self.click(self.recipient_city_locator)
self.click((By.XPATH,f"//div[@x-placement='bottom-start']//li[text()='{city}']"))
# 填写区域信息
self.click(self.recipient_area_locator)
self.click((By.XPATH,f"//div[@x-placement='bottom-start']//li[text()='{area}']"))
# 填写详细地址
self.input_text(self.detail_address_locator, detail_address)
# 点击提交按钮
self.click(self.submit_button_locator)
# 是否添加地址成功,通过提示来进行断言
def add_new_address_tips(self):
return self.get_text(self.add_address_success_tips_locator)
# 添加手机号码作为断言的函数
def is_phone_displayed(self,phone_number):
return self.wait_element_visible((By.XPATH,f"//div[text()='{phone_number}']")).is_displayed()
test_add_address.py文件的内容:
from selenium import webdriver
from d9_fixture.pageobjects.home_page import HomePage
from d9_fixture.pageobjects.login_page import LoginPage
from d9_fixture.pageobjects.user_center_page import PersonCenterPage
def test_is_add_address_success(login):
home_page = HomePage(login)
# 点击个人中心
home_page.click_person_center()
# 在个人中心页面添加地址
user_page = PersonCenterPage(login)
# 添加地址
user_page.write_detail_address("富贵花","18867894326","","东北市","富贵区","城市花园1号楼1单元101室")
assert user_page.add_new_address_tips() == '添加地址成功'
# 添加手机号码断言
assert user_page.is_phone_displayed("18867894326")
test_login.py文件的内容:
from time import sleep
import pytest
from selenium import webdriver
from d9_fixture.pageobjects.home_page import HomePage
from d9_fixture.pageobjects.login_page import LoginPage
def test_login_success(get_driver):
# 点击首页的登录操作
homepage = HomePage(get_driver)
homepage.click_login_link()
# 在登陆页面进行登录操作
loginpage = LoginPage(get_driver)
loginpage.login("uto","123456")
# 断言检测测试是否成功(通过预期结果和实际结果的比较)
# 检查点:1、欢迎页,如果函数的返回值为True,那么断言之后返回是通过的
assert homepage.is_display_welcome_tips()
# assert loginpage.get_login_tips() == '账号或者密码不正确'
assert homepage.get_username_text() == 'uto'
# 对登录结果的断言
def test_login_uncorrect_username(get_driver):
homepage = HomePage(get_driver)
homepage.click_login_link()
# 在登陆页面进行登录操作
login_page = LoginPage(get_driver)
login_page.login("lemon_auto1", "lemon123456")
# 页面登录过程中的提示信息断言
sleep(1)
assert login_page.get_login_tips() == '账号或密码不正确'
login_datas = [{'username':'','password':'123456','tips':'账号为4~16位字母、数字或下划线'},
{'username':'11','password':'123456','tips':'账号为4~16位字母、数字或下划线'},
{'username':'lem','password':'123456','tips':'账号为4~16位字母、数字或下划线'}]
@pytest.mark.parametrize('datas',login_datas)
def test_login_fail(datas,get_driver):
# 点击首页的登录链接
home_page = HomePage(get_driver)
home_page.click_login_link()
# 在登录页面进行登录操作
login_page = LoginPage(get_driver)
login_page.login(datas['username'], datas['password'])
# 用户名输入框的提示 - 断言
assert login_page.get_user_input_error_tips()== datas['tips']
test_submit_order.py文件的内容:
from d9_fixture.pageobjects.home_page import HomePage
from d9_fixture.pageobjects.my_order_page import MyOrderPage
from d9_fixture.pageobjects.product_detail_page import ProductDetailPage
from d9_fixture.pageobjects.submit_order_page import SubmitOrderPage
def test_submit_order(login):
# 登录,点击首页的登录链接
home_page = HomePage(login)
# 点击商品列表
home_page.click_goods_list()
# 搜索选择商品
home_page.search_select_product("Mac")
# 商品详情页
product_detail_page = ProductDetailPage(login)
product_name = product_detail_page.get_product_name()
product_price = product_detail_page.get_product_price()
product_detail_page.buy_now()
# 提交订单
submit_order_page = SubmitOrderPage(login)
submit_order_page.submit_order()
# 进入我的订单页面
home_page.my_order()
my_order_page = MyOrderPage(login)
# 断言
# 1、检查订单商品名称
assert my_order_page.get_product_name() == product_name
# 2、检查订单商品数量
assert my_order_page.get_product_num() == '1'
# 3、检查订单商品价格
assert my_order_page.get_product_price() == product_price
# 4、检查订单的状态
assert my_order_page.get_order_status() == '待支付'