Playwright的封装

简介: 从运行速度而言playwright远远都快于selenium、无头模式快于有头模式

Playwright与selenium都是基于WEB的GUI自动化测试的框架,前面我介绍了selenium的封装,这里来介绍基于Playwright的封装。

1.目录结构

首先仍旧先来介绍目录结构

____________________________________________________________
|______basepage            基础操作目录
|___________base.py            封装基本操作
|___________db.py            封装数据库操作
|___________page.py            封装Playwright page对象
|______data                测试数据目录
|___________data.yaml            数据
|___________data.py            封装对data.yaml数据的获取操作
|______pageobject            页面封装和业务封装目录
|___________flowobject.py        封装业务操作
|___________pageobject.py        封装页面操作
|______test                测试目录
|___________test.py            封装测试
  1. 基础操作(basepage)

basepage目录封装基础操作,包括base.py、db.py和page.py

2.1 base.py

base.py对Playwright专门做了调整,包括base、judge、WaitUtils和异常四个类。分别封装寻找元素、断言和等待方法。

2.1.1 base类

代码如下

class base:
    @staticmethod
    #通过label查找元素
    def find_element_by_label(page,label_str):
        return page.get_by_label(label_str)
    @staticmethod
    #通过text查找元素
    def find_element_by_text(page,text_str):
        return page.get_by_text(text_str)
    @staticmethod
    #通过location查找元素
    def find_element_by_string(page,string):
        return page.locator(string)
    @staticmethod
    #通过表达式查找元素
    def find_element_by_xpath(page,xpath):
        return page.locator("xpath="+xpath)
    @staticmethod
    #通过id查找元素
    def find_element_by_id(page,id):
        return page.locator("#"+id)
    @staticmethod   
    #通过input/name查找元素
    def find_element_by_input_name(page,name):
        return page.locator("input[name="+name+"]")
    @staticmethod
    #通过type和value查找元素
    def find_element_by_type_value(page,mytype,value):
        return page.locator('input[type="'+mytype+'"][value="'+value+'"]')
    @staticmethod
    #通过name和value查找元素
    def find_element_by_name_value(page,name,value):
        return page.locator('input[name="'+name+'"][value="'+value+'"]')
    @staticmethod
    #通过name查找元素
    def find_element_by_name(page,name):
        return page.locator('input[name="'+name+'"]')
    @staticmethod
    #通过表达式查找元素
    def find_element_by_classname(page,classname):
        return page.locator(".="+classname)
    @staticmethod
    #通过role查找元素
    def find_element_by_role(page,role,value):
        return page.get_by_role(role,name=value)
    @staticmethod
    #通过placeholder查找元素
    def find_element_by_placeholder(page,string):
        return page.get_by_placeholder(string)
    @staticmethod
    #通过文本框值
    def get_text_value(page,mytype):
        return page.input_value('input[type='+mytype+']')
    @staticmethod
    #输入,每次输入都需要清除元素
    def fill(element,str):
        element.fill("")
        element.fill(str)
    @staticmethod
    def save_cookies(page):
        """保存 Cookies 和 Local Storage 到文件"""
        # 1. 保存 Cookies
        cookies = page.context.cookies()
        with open("cookies.pkl", "wb") as f:
            pickle.dump(cookies, f)
        # 2. 保存 Local Storage
        localStorage = page.evaluate("() => JSON.stringify(window.localStorage)")
        with open("localstorage.pkl", "wb") as f:
            pickle.dump(localStorage, f)
    @staticmethod
    def load_cookies(page, url):
        """从文件恢复 Cookies 和 Local Storage"""
        try:
            # 1. 加载 Cookies
            with open("cookies.pkl", "rb") as f:
                cookies = pickle.load(f)
                page.context.add_cookies(cookies)
            # 2. 加载 Local Storage
            with open("localstorage.pkl", "rb") as f:
                localStorageStr = pickle.load(f)
                script = f"""
                    (function(storage) {
   {
   
                        try {
   {
   
                            const entries = JSON.parse(storage);
                            for (const [key, value] of Object.entries(entries)) {
   {
   
                                window.localStorage.setItem(key, value);
                            }}
                        }} catch(e) {
   {
   
                            console.error('LocalStorage 恢复失败:', e);
                        }}
                    }})({
   localStorageStr});
                """
                page.evaluate(script)
            # 3. 刷新页面使状态生效
            page.goto(url)
            page.reload()
            page.goto("http://127.0.0.1:8000/goods_view/")
        except Exception as e:
            myexception.my_exception(page,f"状态恢复失败: {str(e)}","error_page.png")
    @staticmethod
    def execute_js_script(page,script):
       return page.evaluate(script)

2.1.2 judge类

代码如下

class judge:
    @staticmethod   
    #判断符合string格式(<td><a href="/update_address/\d/\d/">lable</a></td>)是否存在
    def judge_link_in_td_exists(page,string,lable): 
        # 使用 CSS 选择器查找所有 <td> 下的 <a> 标签
        elements = page.query_selector_all("td a")
        found = False
        for el in elements:
            text = el.text_content().strip()
            href = el.get_attribute("href")            
            if text == lable and re.match(string, href):
                found = True
                break
        return found
    @staticmethod

#通过xpath判断元素是否存在
    def judge_by_xpath(page,string):
        element = base.find_element_by_xpath(page,string)
        assert element.count() > 0,"没有找到xpath="+string
    @staticmethod

#通过页面是否含有text判断
    def judge_by_text(page,field,text):
        assert page.text_content(field) == text
    @staticmethod

#通过页面是否部分含有text判断
    def judge_by_part_text(page,field,text):
        all_text = page.text_content(field)
        if text in all_text:
            assert 1 == 1
        else:
            assert 0 == 1
    @staticmethod

#通过元素是否可见
    def judge_element_is_visible(page,field):
        element = page.locator(field)
        is_visible = element.is_visible()
        assert is_visible, field+"元素不可见"

#断言type的值为value
    def judge_type_value(page,mytype,values):
        i = 0
        for value in(values):
            expect(page.locator('input[type="'+mytype+'"]').nth(i)).to_have_value(value)
            i+=1

2.1.3 WaitUtils类
代码如下

class WaitUtils: 
    @staticmethod
    def wait_for_selector(page,sign,state="attached"):
        page.wait_for_selector(sign, state=state)

2.1.4 异常类

代码如下

class myexception:
    def my_exception(page,info,image):
        print(info)
        page.screenshot(path=image)

2.2 db.py

db.py封装对数据库的操作,对于selenium基本没有发生变化,用于tearDown清理数据库操作。

import pymysql
import sys
sys.path.append("../data")
from data import TestConfig
class ClassDB:
    def connect_db(self):
        host = 'localhost'
        user = 'root'
        password = '123456'
        database = 'ebusiness'
        return pymysql.connect(host=host, user=user, password=password, database=database, charset='utf8')
    def init_db(self,conn):
        config = TestConfig('default')
        self.execute_query("DELETE FROM goods_order ORDER BY id DESC LIMIT 1",conn)
        self.execute_query("DELETE FROM goods_orders ORDER BY id DESC LIMIT 1",conn)
        self.execute_query("DELETE FROM goods_address where address='"+config.address+"'",conn)
        self.execute_query("DELETE FROM goods_user where username='"+config.username+"'",conn)
    def close_db(self,conn):
        try:
            conn.close()
        except MySQLError as e:
            print(f"数据库关闭失败: {e}")
    def execute_query(self, query,conn,params=None):
        with conn.cursor() as cursor:
            cursor.execute(query, params)
            conn.commit()
            return cursor.fetchall()
    def finsh_test(self):
        connection=self.connect_db()
        self.init_db(connection)
        self.close_db(connection)

2.3 page.py

page.py作用类似于selenium中的driver.py

!/usr/bin/env python
#coding:utf-8
import sys
sys.path.append("../data")
from playwright.sync_api import sync_playwright
class mypages:
        def __init__(self,config):
                self.config = config
                browser = self.config.browser
                headless = self.config.headless
                self.playwright = sync_playwright().start()
                if browser.lower() == "chrome":
                        self.browser = self.playwright.chromium.launch(headless=headless)
                if browser.lower() == "firfox":
                        self.browser = self.playwright.firefox.launch(headless=headless)
                if browser.lower() == "edge":
                        self.browser = self.playwright.chromium.launch(headless=headless, channel="msedge")
                self.page = self.browser.new_page()

headless的值取值与data目录中的daya.yaml中,表示是否已有头还是无头方式启动:headless=False表示有头,headless=True表示无头,无头方式运行要比有头速度快。

  1. 数据操作操作(data)

3.1 data.yaml

data.yaml基本没有变化,我仅为了优化程序,加了几个字段

environments:
  default: 
    browser: "chrome" #以什么浏览器运行chrome、firfox、edge
    url: "http://127.0.0.1:8000" #首页URL
    headless: False #有头还是无头方式运行,headless=False表示有头,headless=True表示无头,无头方式运行要比有头速度快
    username: "xixi" #用户名
    password: "654321" #密码
    repassword: "123456" #重置密码
    email: "a@b.com" #Email
    address: "首体南路1号" #配货地址
    phone: "15434564455" #配货电话
    number: "3" #选择商品数量

3.2 data.py

data.py对于data.yaml中的每个字段定义一个读取的方法。

import yaml
import os
class TestConfig:
    def __init__(self, env='default'): 
        current_dir = os.path.dirname(os.path.abspath(__file__))
        config_path = os.path.join(current_dir, 'data.yaml')
        with open(config_path, 'r', encoding='utf-8') as f:
            data = yaml.safe_load(f)
            self.config = data['environments'][env]
    @property
    def browser(self):
        return self.config['browser']
    @property
    def url(self):
        return self.config['url']
    @property
    def headless(self):
        return self.config['headless']
    @property
    def username(self):
        return self.config['username']
    @property
    def password(self):
        return self.config['password']
    @property
    def email(self):
        return self.config['email']
    @property
    def address(self):
        return self.config['address']
    @property
    def phone(self):
        return self.config['phone']
    @property
    def repassword(self):
        return self.config['repassword']
    @property
    def number(self):
        return self.config['number']
  1. 页面和业务操作(pageobject)

4.1 pageobject.py

pageobject.py封装页面操作,在flowobject.py中采用xxx_page.xxx_action的操作形式。包括

  • login_page 登录页类

  • register_page 注册页类

  • goods_list_page 商品列表页类

  • goods_detail_page 商品详情页类

  • chart_page 购物车页类

  • order_page 订单页类

  • user_info_page 用户信息页类

  • add_address_page 添加收货地址页类

  • reset_password_page 重置密码页类

每个类包括元素定位,元素操作,动态等待和断言四类方法。由于某个操作后的定位往往在另外一个页类中,动态等待和断言往往结合成一个方法中,先动态等待,等到后再断言,这个方法定义在另一类中,在本类中调用。比如注册成功后需要在登录界面中断言(注册成功页面跳转到登录页面),在登录页面定义断言方法。

#登录类
class login_page:          
    #判断进入登录页面
    def judge_enter_login_page(page):
judge_enter_login_page(page)是登录类中的一个验证方法,验证是否进入登录页面。在注册页类中
#注册类
class register_page:        
    #注册
    def register(page,username,password,email):
        …
        #验证是否进入登录页面
        login_page.judge_enter_login_page(page)

注册完毕验证是否进入登录页面。代码如下

#!/usr/bin/env python
#coding:utf-8
import sys,time
sys.path.append("../basepage")
from base import base,WaitUtils,judge
#登录类
class login_page:      
    #登录
    def login(page,username,password):
        username_field = base.find_element_by_label(page,"用户名:")
        password_field = base.find_element_by_label(page,"密码:")
        login_button = base.find_element_by_role(page,"button","登录")
        base.fill(username_field,username)
        base.fill(password_field,password)
        login_button.click()
        #判断进入商品列表页面
        goods_list_page.judge_enter_goods_list_page(page,username)

    #进入注册页面
    def enter_register_page(page):
        register_link=base.find_element_by_text(page,"注册")
        register_link.click()
        #判断进入注册页面
        register_page.judge_enter_register_page(page)
    #判断进入登录页面
    def judge_enter_login_page(page):
        WaitUtils.wait_for_selector(page,"#id_username")
        judge.judge_by_text(page,'a[href="/register/"]',"注册")
#注册类
class register_page:    
    #注册
    def register(page,username,password,email):
        username_field= base.find_element_by_label(page,"用户名:")
        password_field= base.find_element_by_label(page,"密码:")
        email_field= base.find_element_by_label(page,"电子邮件:")
        submit_field= base.find_element_by_role(page,"button","注册")
        base.fill(username_field,username)
        base.fill(password_field,password)
        base.fill(email_field,email)
        submit_field.click()
        #验证是否进入登录页面
        login_page.judge_enter_login_page(page)

    #验证是否进入注册页面
    def judge_enter_register_page(page):
        WaitUtils.wait_for_selector(page,"#id_username")
        judge.judge_by_text(page,'a[href="/index/"]',"登录")
#商品列表页面类
class goods_list_page:
    #把商品放入购物车
    def put_into_chart(page):
        input_link = base.find_element_by_xpath(page,"/html/body/div/div[2]/div/table/tbody/tr[1]/td[5]/a")
        input_link.click()
        #验证把商品放入购物车成功
        goods_list_page.judge_put_into_chart_success(page)

    #查询商品
    def serch_good(page,goods):
        search_input = base.find_element_by_placeholder(page,"名称")
        search_button = base.find_element_by_role(page,"button","搜索")
        base.fill(search_input,goods) 
        search_button.click()
        #验证查询商品成功
        goods_list_page.judge_serch_good_success(page)

    #进入查看商品详情页
    def see_goods_detail(page):
        goods_detail_link = base.find_element_by_xpath(page,"/html/body/div/div[2]/div/table/tbody/tr[1]/td[4]/a")
        goods_detail_link.click()
        #验证是否进入商品详情页成功
        goods_detail_page.judge_enter_goods_detail_success(page)

    #进入查看用户信息
    def see_user_info(page,username):
        user_link = base.find_element_by_text(page,username)
        user_link.click()
        #验证是否进入用户信息页成功
        user_info_page.judge_enter_user_info_page_success(page)

    #退出
    def exit(page):
        exit_link = base.find_element_by_text(page,"退出")
        exit_link.click()
        #验证是否进入登录页
        login_page.judge_enter_login_page(page)
    #进入购物车页面
    def enter_chart(page):
        enter_chart_link = base.find_element_by_text(page,"查看购物车")
        enter_chart_link.click()
        #验证是否进入购物车页面
        chart_page.judge_enter_chart_page(page)

    #查看所有订单
    def view_all_orders(page):
        view_all_orders_link = base.find_element_by_text(page,"查看所有订单")
        view_all_orders_link.click()
        #验证是否进入所有订单页
        order_page.judge_enter_all_orders_page(page)
    #验证是否进入商品列表页面
    def judge_enter_goods_list_page(page,username):
        WaitUtils.wait_for_selector(page,".navbar-form")
        judge.judge_by_text(page,'a[href="/user_info/"]',username)
    #验证把商品放入购物车成功
    def judge_put_into_chart_success(page):
        chart_sgin_xpath = "//font[@color='#FF0000' and text()='1']"
        WaitUtils.wait_for_selector(page,chart_sgin_xpath)
        judge.judge_by_part_text(page,'a[href="/view_chart/"]','查看购物车')

    #验证查询商品成功
    def judge_serch_good_success(page):
        WaitUtils.wait_for_selector(page,".form-control")
        judge.judge_by_text(page,'a[href="/view_goods/1/"]',"查看")
#购物车类
class chart_page:  
    #在购物车中更改商品数量
    def change_goods_number(page,number):
        number_field =base.find_element_by_id(page,"id_count")
        change_button=base.find_element_by_type_value(page,"submit","修改")
        base.fill(number_field,number)
        change_button.click()
        #验证修改后的购物车某个商品数量
        chart_page.judge_number_in_chart(page,number)
    #进入选择收货地址页,准备下单
    def select_address(page):
        pay_button = base.find_element_by_type_value(page,"submit","生成订单")
        pay_button.click()
        #验证是否进入选择收货地址页
        order_page.judge_enter_select_address_page(page)

    #验证是否进入购物车页面
    def judge_enter_chart_page(page):
        WaitUtils.wait_for_selector(page,".vIntegerField")
        judge.judge_by_text(page,'a[href="/remove_chart/1/"]',"移除")

    #验证修改后的购物车某个商品数量
    def judge_number_in_chart(page,number):
        get_number = base.get_text_value(page,"number")
        assert get_number == number
#订单类
class order_page:
    #选择收货地址
    def select_address(page,address,phone):
        found = judge.judge_link_in_td_exists(page,r"^/delete_address/\d+/\d+/$","删除")
        if not found: 
        #生成订单
        add_address_button=base.find_element_by_type_value(page,"submit","添加地址")
        add_address_button.click()
        #判断有无进入添加收货地址页
        add_address_page.judge_enter_add_address(page)
        #输入用户信息
        add_address_page.enter_address_info(page,address,phone)
        #判断是否生成收货地址
        user_info_page.judge_having_address(page,"order")  
        #选择订单
        address_field = base.find_element_by_input_name(page,"address")
        next_button=base.find_element_by_type_value(page,"submit","下一步")
        address_field.click()
        next_button.click()
        order_page.judge_in_order(page)

    #验证这条订单是否生成
    def judge_in_order(page):
        WaitUtils.wait_for_selector(page,"td a:has-text('删除')")
        judge.judge_by_text(page,"td a:has-text('删除')","删除")
        #验证查看所有订单是否有这条订单
        goods_list_page.view_all_orders(page)

    #验证是否进入选择收货地址页
    def judge_enter_select_address_page(page):
        WaitUtils.wait_for_selector(page,".col-md-6")
        judge.judge_element_is_visible(page,"form[method='POST']")

    #验证是否进入所有订单页
    def judge_enter_all_orders_page(page):
        WaitUtils.wait_for_selector(page,"td a:has-text('删除')")
        judge.judge_by_text(page,"td a:has-text('删除')","删除")
#用户信息类
class user_info_page:
    #从用户信息页面进入添加用户地址页面
    def enter_add_adress(page):
        add_address_button=base.find_element_by_role(page,"button","添加地址")
        add_address_button.click()
        #验证是否进入添加用户地址页面
        add_address_page.judge_enter_add_address(page)

    #从用户信息页面进入修改密码
    def reset_password(page):
        enter_reset_password_button = base.find_element_by_type_value(page,"submit","修改密码")
        enter_reset_password_button.click() 
        #判断是否进入重置密码页面
        reset_password_page.judge_enter_reset_password_page(page)

    #查看进入用户信息页成功
    def judge_enter_user_info_page_success(page):
        WaitUtils.wait_for_selector(page,"#add_address")
        mystr=["修改密码","添加地址"]
        judge.judge_type_value(page,"submit",mystr)

    #判断判断是否生成收货地址
    def judge_having_address(page,sign):
        if sign=="order":
            judge.judge_element_is_visible(page,"input[type='radio']")
        elif sign=="address":
            found = judge.judge_link_in_td_exists(page,r"^/update_address/\d+/\d+/$","修改")
            assert found, "未找到符合条件的 '"+lable+"' 链接"
        else:
            print("sign in fuction user_info_page.judge_having_address(page,sign) is error")
#添加收货地址类
class add_address_page:
    #输入用户地址信息
    def enter_address_info(page,address,phone):
        address_field=base.find_element_by_name(page,"address")
        phone_field=base.find_element_by_name(page,"phone")
        submit_button=base.find_element_by_role(page,"button","提交")
        base.fill(address_field,address)
        base.fill(phone_field,phone)
        submit_button.click()
        #判断有无用户地址信息
        user_info_page.judge_having_address(page,"address")

    #判断有无进入添加收货地址页
    def judge_enter_add_address(page):
        WaitUtils.wait_for_selector(page,"#id_address")
        judge.judge_by_text(page,'button',"提交")
#商品详情类
class goods_detail_page:       

    #查看进入商品详情页成功
    def judge_enter_goods_detail_success(page):
        WaitUtils.wait_for_selector(page,".input-group")
        judge.judge_by_text(page,'a[href="/add_chart/1/2/"]',"放入购物车")
#重置密码类
class reset_password_page:

    #重置密码
    def reset_password(page,password,repassword):
        old_password_field=base.find_element_by_name(page,"oldpassword")
        new_password_field=base.find_element_by_name(page,"newpassword")
        renew_password_field=base.find_element_by_name(page,"checkpassword")
        submit_button=base.find_element_by_type_value(page,"submit","修改")
        base.fill(old_password_field,password)
        base.fill(new_password_field,repassword)
        base.fill(renew_password_field,repassword)
        submit_button.click()
        #重置密码成功
        reset_password_page.reset_password_success(page)

    #重置密码成功
    def reset_password_success(page):
        judge.judge_by_text(page,'p[style="color:red"]',"密码修改成功")

    #判断是否进入重置密码页面
    def judge_enter_reset_password_page(page):
        WaitUtils.wait_for_selector(page,'input[name="oldpassword"]')
        value = ["修改"]
        judge.judge_type_value(page,"submit",value)

4.2 flowobject.py
flowobject.py封装业务流操作test.py,在test/test.py中采用xxx_flow.xxx_business的操作形式。在这里定义了如下几个类。

1)userFlow: 用户流

  • Register_and_Login(self) 注册然后登录

  • reset_password(self) 重置密码

2)goodsFlow: 商品流

  • search_goods(self,goods) 查询商品

  • put_goods_into_chart(self) 放入购物车

  • see_goods_detail(self) 查看商品详情

3)addressFlow: 收货地址流

  • add_address(self) 添加收货地址

4)orderFlow: 订单流

  • pay_order(self) 下单

代码如下

#!/usr/bin/env python
#coding:utf-8
import sys
sys.path.append("../basepage")
from pageobject import *
from base import myexception
class userFlow:
    def __init__(self, config, page):
        self.config = config
        self.page = page
    #注册然后登录
    def Register_and_Login(self):
        try:
            self.page.goto(self.config.url)
            #进入注册页
            login_page.enter_register_page(self.page)
        except Exception as e:
            myexception.my_exception(self.page,f"进入注册页失败: {str(e)}","enter_register_page_fail.png")
        try:
            #注册用户
            register_page.register(self.page,self.config.username, self.config.password, self.config.email)
        except Exception as e:
            myexception.my_exception(self.page,f"注册失败: {str(e)}","register_fail.png")
        try:
            #用注册用户登录
            login_page.login(self.page,self.config.username, self.config.password)
        except Exception as e:
            myexception.my_exception(self.page,f"登录失败: {str(e)}","login_fail.png")
    #重置密码
    def reset_password(self):
        try:  
            #进入查看用户信息
            goods_list_page.see_user_info(self.page,self.config.username)
        except Exception as e:
            myexception.my_exception(self.page,f"进入用户详情页失败: {str(e)}","enter_user_info_page_fail.png")
        try:
            #从用户信息页面进入修改密码
            user_info_page.reset_password(self.page)
        except Exception as e:
            myexception.my_exception(self.page,f"进入修改页密码失败: {str(e)}","enter_reset_password_page_fail.png")
            #判断是否进入重置密码页面
        try:
            #重置密码
            reset_password_page.reset_password(self.page,self.config.password,self.config.repassword)
        except Exception as e:
            myexception.my_exception(self.page,f"重置密码失败: {str(e)}","reset_password_fail.png")
        try:
            #退出系统
            goods_list_page.exit(self.page)
        except Exception as e:
            myexception.my_exception(self.page,f"退出系统失败: {str(e)}","exit_fail.png")
        try:
            #用重置密码登录
            login_page.login(self.page,self.config.username,self.config.repassword)
        except Exception as e:
            myexception.my_exception(self.page,f"用重置密码登录失败: {str(e)}","login_using_new_password_fail.png")
class goodsFlow:
    def __init__(self, config, page):
        self.config = config
        self.page = page
    #查询商品
    def search_goods(self,goods):
        try:
            #查询商品
            goods_list_page.serch_good(self.page,goods)  
        except Exception as e:
            myexception.my_exception(self.page,f"搜索失败: {str(e)}","search_goods_failed.png")
    #放入购物车
    def put_goods_into_chart(self): 
        try:
            #把商品放入购物车
            goods_list_page.put_into_chart(self.page)        
        except Exception as e:
            myexception.my_exception(self.page,f"放入购物车失败: {str(e)}","put_into_char_failed.png")
    #查看商品详情
    def see_goods_detail(self):
        try:
            #进入查看商品详情
            goods_list_page.see_goods_detail(self.page)
        except Exception as e:
            myexception.my_exception(self.page,f"查看商品详情失败: {str(e)}","see_goods_detail_failed.png")
class addressFlow:
    def __init__(self, config, page):
        self.config = config
        self.page = page
    #添加收货地址
    def add_address(self):
        try:  
            #进入查看用户信息
            goods_list_page.see_user_info(self.page,self.config.username)
        except Exception as e:
            myexception.my_exception(self.page,f"进入用户详情页失败: {str(e)}","see_user_info_fail.png")
        try:
            #从用户信息页面进入添加用户地址页面
            user_info_page.enter_add_adress(self.page)
        except Exception as e:
            myexception.my_exception(self.page,f"进入添加地址页失败: {str(e)}","enter_add_address_fail.png")
        try:
            #输入用户地址信息
            add_address_page.enter_address_info(self.page,self.config.address,self.config.phone)
        except Exception as e:
            myexception.my_exception(self.page,f"进入添加地址页失败: {str(e)}","enter_add_address_fail.png")
class orderFlow:
    def __init__(self, config, page):
        self.config = config
        self.page = page

#下单
    def pay_order(self):
        try:
            #把商品放入购物车
            goods_list_page.put_into_chart(self.page)        
        except Exception as e:
            myexception.my_exception(self.page,f"放入购物车失败: {str(e)}","put_into_char_failed.png")
        try:
            #进入购物车
            goods_list_page.enter_chart(self.page)
        except Exception as e:
            myexception.my_exception(self.page,f"进入购物车失败: {str(e)}","enter_chart_fail.png")
        try:    
            #在购物车总更改商品数量
            chart_page.change_goods_number(self.page,self.config.number)
        except Exception as e:
            myexception.my_exception(self.page,f"修改商品数量失败: {str(e)}","change_number_fail.png")
        try:
            #进入选择收货地址页,准备下单
            chart_page.select_address(self.page)
        except Exception as e:
            myexception.my_exception(self.page,f"选择收货地址页,准备下单失败: {str(e)}","prepare_pay_order_fail.png")
        try:
            #选择收货地址
            order_page.select_address(self.page,self.config.address,self.config.phone)
        except Exception as e:
            myexception.my_exception(self.page,f"选择收货地址失败: {str(e)}","select_address_fail.png")
  1. 测试操作(test)

test目录下为测试代码

#!/usr/bin/env python
#coding:utf-8
import sys
sys.path.append("../data")
sys.path.append("../basepage")
sys.path.append("../pageobject")
import unittest
from page import mypages
from base import base
from db import ClassDB
from data import TestConfig
from flowobject import *
class CheckEbusiness(unittest.TestCase):
        def setUp(self):
                self.config = TestConfig('default')
                page = mypages(self.config)
                self.page = page.page
                self.browser = page.browser
                self.playwright = page.playwright
                self.userFlow = userFlow(self.config,self.page)
                self.goodsFlow = goodsFlow(self.config,self.page)
                self.addressFlow = addressFlow(self.config,self.page)
                self.orderFlow = orderFlow(self.config,self.page)
    # 测试注册和登录
        def test_Register_Login(self):
                self.userFlow.Register_and_Login()
                base.save_cookies(self.page)
        #测试查询商品
        def test_search(self):
                base.load_cookies(self.page,self.config.url)
                self.goodsFlow.search_goods("茶")
        #测试放入购物车
        def test_put_into_chart(self):
                base.load_cookies(self.page,self.config.url)
                self.goodsFlow.put_goods_into_chart()
        #测试查看商品详情
        def test_see_goods_detail(self):
                base.load_cookies(self.page,self.config.url)
                self.goodsFlow.see_goods_detail()
        #测试修改密码
        def test_reset_password(self):
                base.load_cookies(self.page,self.config.url)
                self.userFlow.reset_password()
        #测试新加地址
        def test_add_address(self):
                base.load_cookies(self.page,self.config.url)
                self.addressFlow.add_address()
        #测试下单
        def test_pay_order(self):
                base.load_cookies(self.page,self.config.url)
                self.orderFlow.pay_order()
        def tearDown(self):
                self.browser.close()
                self.playwright.stop()
        @classmethod
        def tearDownClass(cls):
                try:
                        db = ClassDB() 
                        db.finsh_test()
                except:
                        pass
if __name__ == '__main__':
        suite=unittest.TestSuite()
        suite.addTest(CheckEbusiness("test_Register_Login"))
        suite.addTest(CheckEbusiness("test_search"))
        suite.addTest(CheckEbusiness("test_put_into_chart"))
        suite.addTest(CheckEbusiness("test_see_goods_detail"))
        suite.addTest(CheckEbusiness("test_add_address"))
        suite.addTest(CheckEbusiness("test_reset_password"))
        suite.addTest(CheckEbusiness("test_pay_order"))
        runner=unittest.TextTestRunner()
        runner.run(suite)
  1. 比较

最后让我们比较一下selenium、playwright有头模式和无头模式运行电子商务产品7个用例的时间。
image.png
由此可见从运行速度而言playwright远远都快于selenium、无头模式快于有头模式。

顾翔凡言:人工智能未来的发展瓶颈在于对知识的更新。唯一不变的是变化,知识发生了变化,人工智能软件能否及时跟进变化,可能阻碍人工智能的使用。

目录
相关文章
|
8天前
|
人工智能 运维 安全
|
6天前
|
人工智能 异构计算
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
|
7天前
|
机器学习/深度学习 人工智能 自然语言处理
B站开源IndexTTS2,用极致表现力颠覆听觉体验
在语音合成技术不断演进的背景下,早期版本的IndexTTS虽然在多场景应用中展现出良好的表现,但在情感表达的细腻度与时长控制的精准性方面仍存在提升空间。为了解决这些问题,并进一步推动零样本语音合成在实际场景中的落地能力,B站语音团队对模型架构与训练策略进行了深度优化,推出了全新一代语音合成模型——IndexTTS2 。
642 22
|
7天前
|
人工智能 测试技术 API
智能体(AI Agent)搭建全攻略:从概念到实践的终极指南
在人工智能浪潮中,智能体(AI Agent)正成为变革性技术。它们具备自主决策、环境感知、任务执行等能力,广泛应用于日常任务与商业流程。本文详解智能体概念、架构及七步搭建指南,助你打造专属智能体,迎接智能自动化新时代。
|
13天前
|
人工智能 JavaScript 测试技术
Qwen3-Coder入门教程|10分钟搞定安装配置
Qwen3-Coder 挑战赛简介:无论你是编程小白还是办公达人,都能通过本教程快速上手 Qwen-Code CLI,利用 AI 轻松实现代码编写、文档处理等任务。内容涵盖 API 配置、CLI 安装及多种实用案例,助你提升效率,体验智能编码的乐趣。
1045 110
人工智能 数据可视化 数据挖掘
235 0