使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 31--开源电商商城系统项目实战--加入购物车、提交订单测试场景

测试学习记录,仅供参考!

项目实战 4(开源电商商城系统项目)

测试场景

商品加入购物车成功之后提交订单去支付的场景;

购物车页面

1、首先,打开开源电商商城系统首页,第一步要先登录(测试账号);

2、登录成功之后打开首页或者等待(3S)时间到了之后自动返回首页;

3、查看购物车(自行添加设置测试商品);

4、购物车页面点击"结算中心"图片按钮;

5、填写收货人信息;

6、完善收货人信息后单击"配送至这个地址"按钮;

7、提交订单页面;

页面类
页面元素

购物车页面元素定位

8、 在项目根目录 pageObject 软件包下新建一个名称为 flow_page 的目录文件,在 flow_page 目录文件下新建名称一个为 flow_page.py 的 Python 文件;把"购物车"页面的定位元素放到页面类中去管理;

复制代码
# 导包
from selenium.webdriver.common.by import By
from util_tools.basePage import BasePage

# 购物车页面
class FlowPage(BasePage):
    # url
    url = '/flow.php'
    # 结算中心按钮
    settle_button = (By.XPATH, '//*[@alt="checkout"]')
    # 继续购物按钮
    continue_button = (By.XPATH, '//*[@alt="continue"]')

    # 收货人信息--配送区域
    country = (By.NAME, 'country')      # 国家
    province = (By.NAME, 'province')    # 省份
    city = (By.NAME, 'city')            # 城市
    district = (By.NAME, 'district')    # 区县

    # 收货人信息
    consignee = (By.NAME, 'consignee')  # 收货人姓名
    address = (By.NAME, 'address')      # 详细地址
    tel = (By.NAME, 'tel')              # 电话
    email = (By.NAME, 'email')          # 电子邮件地址

    # 配送至这个地址
    submit = (By.NAME, 'Submit')
    # 配送方式
    shipping_radio = (By.NAME, 'shipping')
    # 支付方式
    payment_radio = (By.NAME, 'payment')
    # 提交订单
    submit_order = (By.XPATH, '//input[@type="image"]')

9、 一般情况下加入购物车去提交订单相关的页面类元素完成之后是开始页面操作;但是这里先封装一个判断元素是否存在 def is_element_present(self, locator: tuple): 方法;

封装方法

在基类公共方法模块中添加;

10、 优化项目根目录 util_tools 软件包下 basePage.py 文件,封装 判断元素是否存在 方法;

复制代码
# 导包
from selenium import webdriver
from selenium.common import NoSuchElementException, TimeoutException
from selenium.webdriver.common.by import By
from time import sleep
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.wait import WebDriverWait
from config import setting
from util_tools.logs_util.recordlog import logs
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from datetime import datetime
import pytesseract
from PIL import Image
from selenium.webdriver.support.ui import Select
from util_tools.handle_data.configParse import ConfigParse


class BasePage(object):


    def __init__(self, driver):
        self.__driver = driver
        self.__wait = WebDriverWait(self.__driver, setting.WAIT_TIME)
        self.conf = ConfigParse()

    def window_max(self):
        self.__driver.maximize_window()

    def window_full(self):
        self.__driver.fullscreen_window()

    def screenshot(self):
        self.__driver.get_screenshot_as_png()

    def open_url(self, url):
        if url.startswith('http') or url.startswith('https'):
            self.__driver.get(url)
            logs.info(f'打开页面:{url}')
        else:
            new_url = self.conf.get_host('host') + url
            self.__driver.get(new_url)
            logs.info(f'打开页面:{new_url}')

    @property
    def current_url(self):
        return self.__driver.current_url

    @property
    def title(self):
        return self.__driver.title

    def refresh(self):
        self.__driver.refresh()

    @property
    def switch_to(self):
        return self.__driver.switch_to

    def iframe(self, frame):
        self.switch_to.frame(frame)

    def exit_iframe(self):
        self.switch_to.default_content()

    @property
    def alert(self):
        return self.__wait.until(ec.alert_is_present())

    def alert_confirm(self):
        self.alert.accept()

    def alert_cancel(self):
        self.alert.dismiss()

    def location_element(self, by, value):
        try:
            element = self.__wait.until(ec.presence_of_element_located((by, value)))
            logs.info(f"找到元素:{by}={value}")
            return element
        except Exception as e:
            logs.error(f"未找到元素:{by}={value}")
            raise e

    def location_elements(self, by, value):
        try:
            self.__wait.until(ec.presence_of_all_elements_located((by, value)))
            elements = self.__driver.find_elements(by, value)
            logs.info(f"找到元素列表:{by}={value}")
            return elements
        except Exception as e:
            logs.error(f"未找到元素列表:{by}={value}")
            raise e

    def click(self, locator: tuple, force=False):
        try:
            element = self.location_element(*locator)
            if not force:
                self.__driver.execute_script("arguments[0].click()", element)
            else:
                self.__driver.execute_script("arguments[0].click({force:true})", element)
            logs.info(f"元素被点击:{locator}")
        except NoSuchElementException as e:
            logs.error(f"元素无法定位:{e}")
            raise e

    def send_keys(self, locator: tuple, data):
        try:
            element = self.location_element(*locator)
            element.send_keys(data)
            logs.info(f"元素被输入内容:{locator},输入的内容为:{data}")
        except NoSuchElementException as e:
            logs.error(f"元素无法定位:{e}")
            raise e

    def selects(self, locator: tuple, index):
        try:
            select = Select(self.location_element(*locator))
            select.select_by_index(index)
            logs.info(f'选择第{index}个数据')
        except NoSuchElementException as e:
            logs.error(f'元素无法定位:{e}')
            raise e

    def enter(self):
        try:
            ActionChains(self.__driver).send_keys(Keys.ENTER).perform()
            logs.info("按下回车键")
        except NoSuchElementException as e:
            logs.error(f"元素无法定位:{e}")
            raise e

    def right_click(self, locator: tuple):
        try:
            element = self.location_element(*locator)
            ActionChains(driver).context_click(element).perform()
            logs.info("执行鼠标右键点击操作")
        except NoSuchElementException as e:
            logs.error(f"元素无法定位:{e}")
            raise e

    def double_click(self, locator: tuple):
        try:
            element = self.location_element(*locator)
            ActionChains(driver).double_click(element).perform()
            logs.info("执行鼠标双击操作")
        except NoSuchElementException as e:
            logs.error(f"元素无法定位:{e}")
            raise e

    def screenshots(self, image_name):
        import os
        current_time = datetime.now().strftime("%Y%m%d%H%M%S")
        file_name = f"{image_name}-{current_time}.png"
        file_path = os.path.join(setting.FILE_PATH.get('screenshot'), file_name)
        self.__driver.get_screenshot_as_file(file_path)

    def clear(self, locator: tuple):
        try:
            element = self.location_element(*locator)
            element.clear()
            logs.info("清空文本")
        except NoSuchElementException as e:
            logs.error(f"元素无法定位:{e}")
            raise e

    def ocr_captcha(self, locator: tuple):
        captcha_element = self.location_element(*locator)
        captcha_path = setting.FILE_PATH['screenshot'] + '/captcha.png'
        captcha_element.screenshot(captcha_path)
        captcha_image = Image.open(captcha_path)
        try:
            captcha_text = pytesseract.image_to_string(captcha_image)
            logs.info(f"识别到的验证码为:{captcha_text}")
            return captcha_text
        except pytesseract.pytesseract.TesseractNotFoundError:
            logs.error("找不到tesseract,这是因为pytesseract模块依赖于TesseractOCR引擎来进行图像识别!")

    def is_element_present(self, locator: tuple):
        try:
            self.__wait.until(ec.presence_of_element_located(*locator))
            return True
        except:
            return False

    def assert_is_element_present(self, locator: tuple):
        try:
            element = self.__driver.find_element(*locator)
            assert element.is_displayed(), '元素不存在'
        except NoSuchElementException as e:
            logs.error(f'元素未找到:{e}')
            raise AssertionError('元素不存在')

    def assert_element_not_visible(self, locator: tuple):
        try:
            self.__wait.until(ec.invisibility_of_element_located(locator))
        except TimeoutException:
            logs.error('元素可见')

    def assert_title(self, expect_title):
        assert expect_title in self.title
页面操作

11、开始页面操作,继续在 flow_page.py 文件编写页面操作相关代码;

复制代码
# 导包
from selenium.webdriver.common.by import By
from util_tools.basePage import BasePage
from time import sleep

# 购物车页面
class FlowPage(BasePage):
    # url
    url = '/flow.php'
    # 结算中心按钮
    settle_button = (By.XPATH, '//*[@alt="checkout"]')
    # 继续购物按钮
    continue_button = (By.XPATH, '//*[@alt="continue"]')

    # 收货人信息--配送区域
    country = (By.NAME, 'country')      # 国家
    province = (By.NAME, 'province')    # 省份
    city = (By.NAME, 'city')            # 城市
    district = (By.NAME, 'district')    # 区县

    # 收货人信息
    consignee = (By.NAME, 'consignee')  # 收货人姓名
    address = (By.NAME, 'address')      # 详细地址
    tel = (By.NAME, 'tel')              # 电话
    email = (By.NAME, 'email')          # 电子邮件地址

    # 配送至这个地址
    submit = (By.NAME, 'Submit')
    # 配送方式
    shipping_radio = (By.NAME, 'shipping')
    # 支付方式
    payment_radio = (By.NAME, 'payment')
    # 提交订单
    submit_order = (By.XPATH, '//input[@type="image"]')

    # 失败操作
    def flow_failed(self):
        """点击结算中心,购物车中没有任何商品"""
        # 打开页面
        self.open_url(self.url)
        # 点击结算中心按钮
        self.click(self.settle_button)

    # 成功操作
    def flow_success(self):
        # 打开页面
        self.open_url(self.url)
        # 点击结算中心
        self.click(self.settle_button)
        # 判断元素是否存在--里面传收货人信息的任意相关字段
        if self.is_element_present(self.country):
            # 选择配送区域
            self.selects(self.country, 1)
            self.selects(self.province, 2)
            self.selects(self.city, 3)
            self.selects(self.district, 4)
            # 输入收货人信息--(自行参数化处理)
            self.send_keys(self.consignee, 'test张三')
            self.send_keys(self.address, '测试详细地址01号')
            self.send_keys(self.tel, '13111111111')
            self.send_keys(self.email, '123@qq.com')
            # 添加强制等待时间查看效果
            sleep(3)
            # 点击配送至这个地址按钮
            self.click(self.submit)
        # 选择配送方式
        self.click(self.shipping_radio)
        # 选择支付方式
        self.click(self.payment_radio)
        sleep(3)
        # 提交订单
        self.click(self.submit_order)

做 web 自动化测试主要就是比较繁琐,需要不停的去定位元素,然后去处理各种各样的场景;

测试类

12、在项目根目录 testcase 软件包下新建一个名称为 flow 的目录文件,在 flow 目录文件下新建一个名称为 test_flow.py 的 Python 文件;

复制代码
# 导包
from pageObject.flow_page.flow_page import FlowPage
from time import sleep

class TestFlow:

    # 失败场景,购物车没有商品时
    def test_purchase_failure(self, get_driver):
        flow_page = FlowPage(get_driver)
        flow_page.flow_failed()
        # 断言没有商品时跳转的url页面地址
        assert flow_page.current_url == 'http://localhost:8088/ecshop/flow.php?step=checkout'
        sleep(3)

    # 成功操作,购物车有商品
    def test_purchase_success(self, get_driver):
        flow_page = FlowPage(get_driver)
        flow_page.flow_success()
        # 订单提交成功,断言url
        assert flow_page.current_url == 'http://localhost:8088/ecshop/flow.php?step=done'
        sleep(3)
运行调试

13、此时运行调试测试查看,能够发现没有登录成功的前提下,是不会进入到"购物车、提交订单"页面的,因为商品是需要登录状态下才能显示出来的,所以会测试运行失败;

14、把登录成功的测试用例设置为优先执行,看能不能提交订单成功;

复制代码
import pytest
from selenium.webdriver.common.by import By
from time import sleep
from pageObject.login_page.login_page import LoginPage
from util_tools.handle_data.operateJson import read_json

class TestLogin:

    # 登录成功
    @pytest.mark.run(order=1)
    # 使用参数化实现一条测试用例跑多种场景--可迭代对象里面有多少组数据就跑多少次测试用例
    @pytest.mark.parametrize('data', read_json('./data/login_success.json'))
    # 用一个 data(需与上面保持一致) 参数去接收参数化数据
    def test_login_success(self, get_driver, data):
        # 进行列表解包--通过两个参数去接收列表中的数据
        username, password = data
        # 传浏览器对象--再把结果返回
        login_page = LoginPage(get_driver)
        # 直接调用页面类中的 login 操作--里面需要输入两个参数(参数化传两个参数)
        login_page.login(username, password)
        # 断言结果
        login_page.assert_is_element_present(login_page.assert_result)
        sleep(2)

    # 登录失败
    @pytest.mark.parametrize('data', read_json('./data/login_failed.json'))
    def test_login_failed(self, get_driver, data):
        username, password = data
        login_page = LoginPage(get_driver)
        login_page.login(username, password)
        login_page.assert_title('系统提示')
        sleep(2)

15、运行调试测试,可以发现测试用例会先去跑登录,然后才去跑其他的测试用例;购物车没有商品的场景测试用例是成功的,但是"提交订单"的测试用例仍是没有成功,查看日志可以发现是没有找到"配送方式",没有定位到元素说明就没有进入到提交订单页面,没有登录状态,无登录状态是提交不了订单的;在测试时,会碰到很多这种场景,测试登录之后,页面的其他功能得在登录状态才能进行继续测试;

保持登录状态下测试其他功能(相当重要,需掌握怎么去保持登录状态和非登录状态)

未完待续。。。

相关推荐
小菜鸟派大星1 小时前
电路学习(九)MOS管
学习·硬件·mos管·电路·电路仿真
梨落秋霜1 小时前
Python入门篇【输入input】
开发语言·python
点云SLAM1 小时前
Discrepancy 英文单词学习
人工智能·学习·英文单词学习·雅思备考·discrepancy·不一致、不协调·矛盾
Buxxxxxx1 小时前
DAY 34 模块和库的导入
开发语言·python
qq_356196951 小时前
day30函数专题1:函数定义和参数@浙大疏锦行
python
haiyu_y1 小时前
Day 27 通用机器学习流水线
人工智能·python·机器学习
立志成为大牛的小牛1 小时前
数据结构——五十三、处理冲突的方法——拉链法(王道408)
数据结构·学习·考研·算法
CHANG_THE_WORLD1 小时前
“元组“名称的由来
python