用户端App自动化测试

news2024/12/28 20:52:07

一、自动化用例录制

1、Appium Inspctor 功能介绍

  • UI 分析
  • 录制用例
  • 元素查找测试
  • Attcah 已有的 session
  • 云测试

2、用例录制

1)获取 app 的信息
2)配置待测应用

3、获取 app 的信息

1)app 入口,两种方式获取:
* 通过 logcat 日志获取
    Mac/Linux: adb logcat ActivityManager:I | grep “cmp"
    Windows: adb logcat ActivityManager:I | findstr "cmp"
* 通过 aapt 获取
    Mac/Linux: aapt dump badging wework.apk | grep launchable-activity
    Windows: aapt dump badging wework.apk | findstr launchable-activity
2)启动应用命令 adb shell am start -W -n <package-name>/<activity-name> -S

4、配置待测应用

1)platformName:平台,Android/iOS
2)deviceName:设备名
3)appPackage:应用的包名
4)appActivity:应用的页面名 Activity
5)noReset: 防止清空缓存信息

5、Appium inspector 页面结构

 

6、功能键

1)SelectElements:选中元素,查看层级和属性
2)Swipe By Coordinates:通过坐标点滑动
3)Tap By Coordinates:通过坐标点点击
4)Back:返回
5)Refresh Source & Screenshot:刷新页面
6)StartRecording:开始录制脚本
7)Search for element:搜索元素
8)Copy XML Source to Clipboard:复制 xml 结构
9)Quit Session & Close Inspector:退出当前 Session

 

7、实战示例

  • 下载官方 Demo apk(github.com/appium/appi…
  • 安装 API Demo.apk

8、录制用例

1)打开 API Demo 应用
2)点击 OS,进入下个界面
3)点击【Morse Code】
4)输入内容【ceshiren.com】
5)返回上一个页面
6)返回上一个页面
7)关闭应用
from appium import webdriver

# 创建一个字典,desirecapbility
caps = {}
# Android 包名和页面名,获取命令:
# mac/linux: adb logcat ActivityManager:I | grep "cmp"
# windows: adb logcat ActivityManager:I | findstr "cmp"
caps["platformName"] = "Android"
caps["appPackage"] = "io.appium.android.apis"
caps["appActivity"] = ".ApiDemos"
caps["deviceName"] = "127.0.0.1:6155"
caps["ensureWebviewsHavePages"] = True

# 创建driver ,与appium server建立连接,返回一个 session
driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)

el1 = driver.find_element_by_accessibility_id("OS")
el1.click()
el2 = driver.find_element_by_accessibility_id("Morse Code")
el2.click()
el3 = driver.find_element_by_id("io.appium.android.apis:id/text")
el3.clear()
el4 = driver.find_element_by_id("io.appium.android.apis:id/text")
el4.send_keys("ceshiren.com")
# 返回
driver.back()
driver.back()
driver.back()
# 回收session
driver.quit()

二、自动化测试用例结构分析

1、 用例脚本优化

  • 添加 capability 信息

  • 初始化webdriver,添加setupteardown

  • 添加隐式等待和noReset属性增强用例稳定性

  • 添加断言

  • 注意

    • selenium 版本建议 3.141.0
    • appium-python-client 版本建议 1.2.0

完整代码

from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy


class TestAppDemo:
    def setup(self):
        # 创建一个字典,desirecapbility
        caps = {}
        caps["platformName"] = "Android"
        # Android 包名和页面名,获取命令:
        # mac/linux: adb logcat ActivityManager:I | grep "cmp"
        # windows: adb logcat ActivityManager:I | findstr "cmp"
        caps["appPackage"] = "io.appium.android.apis"
        caps["appActivity"] = ".ApiDemos"
        caps["deviceName"] = "127.0.0.1:6555"
        caps["noReset"] = "true"

        # 创建driver ,与appium server建立连接,返回一个 session
        # driver 变成self.driver 由局部变量变成实例变量,就可以在其它的方法中引用这个实例变量了
        self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
        self.driver.implicitly_wait(5)

    def teardown(self):
        # 回收session
        self.driver.quit()

    def test_input(self):
        """
        1、打开 API demo apk
        2、点击 OS 控件
        3、点击 Morse Code 控件
        4、在搜索框中输入 ceshiren.com
        5、返回到第一页
        6、断言
        :return:
        """
        # 点击OS控件
        # el1 = self.driver.find_element_by_accessibility_id("OS")
        el1 = self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "OS")
        el1.click()
        # 点击 Morse Code 控件
        # el2 = self.driver.find_element_by_accessibility_id("Morse Code")
        el2 = self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "Morse Code")
        el2.click()
        # 输入`ceshiren.com`
        # el3 = self.driver.find_element_by_id("io.appium.android.apis:id/text")
        el3 = self.driver.find_element(AppiumBy.ID, "io.appium.android.apis:id/text")
        # 清除原有的内容
        el3.clear()
        # 输入内容
        el3.send_keys("ceshiren.com")
        el3.clear()
        # 返回
        self.driver.back()
        # 返回
        self.driver.back()
        # 返回第一页
        self.driver.back()
        # 选择元素进行断言
        result = self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "Accessibility").text
        # 断言
        assert result == "Accessibility"

三、 capability 配置参数解析

1、 Capability 简介

  • 功能:配置 Appium 会话,告诉 Appium 服务器需要自动化的平台的应用程序

  • 形式:键值对的集合,键对应设置的名称,值对应设置的值

  • 主要分为三部分

    • 公共部分
    • ios 部分
    • android 部分

2、 Session

  • Appium 的客户端和服务端之间进行通信的前提
  • 通过 Desired Capabilities 建立会话

3、 公共部分参数配置

 

image

 

image

4、 Android 部分特有参数配置

 

 

5、 Android 部分特有参数配置

 

 

 6、 iOS 独有

 

7、 Desire capability 参数

  • API Demo 启动页配置

 

{
  "platformName": "android",
  "deviceName": "emulator-5554",
  "appPackage": "io.appium.android.apis",
  "appActivity": ".ApiDemos"
}

8、 配置优化

  • 添加参数,提高用例的稳定性
{
  "noReset": "true", // 不清空缓存信息
  "dontStopAppOnReset": "true", // 首次启动的时候,不停止app
  "skipDeviceInitialization": "true", // 跳过安装,权限设置等操作
  "unicodeKeyBoard": "true" // 输入中文
}

四、app自动化控制

1、 启动

  • 启动应用
  • 方式一:webdriver.remote("url",desirecapability)
  • 方式二:launch_app() 将应用启动起来
# 方式一:
self.driver = webdriver.Remote\
("http://127.0.0.1:4723/wd/hub", desire_cap)

# 方式二:热启动,会进入到app的首页
self.driver.launch_app()

2、 数据清理

  • 清空输入框内容

    • clear()
self.driver.find_element_by_accessibility_id('SomeAccessibilityID').clear()

3、 关闭

  • 退出app

    • quit()
self.driver.quit()
五、常见控件定位方法
1、 android 基础知识


Android 是通过容器的布局属性来管理子控件的位置关系,布局关系就是把界面上的所有的空间,根据他们的间距的大小,摆放在正确的位置


Android 七大布局

LinerLayout(线性布局)
RelativeLayout(相对布局)
FrameLayout(帧布局)
AboluteLayout(绝对布局)
TableLayout(表格布局)
GridLayout(网格布局)
ConstraintLayout(约束布局



2、Android 四大组件

activity 与用户交互的可视化界面
service 实现程序后台运行的解决方案
content provider 内容提供者,提供程序所需要的数据
broadcast receiver 广播接收器,监听外部事件的到来(比如来电)

3、常用的控件

TextView(文本控件),EditText(可编辑文本控件)
Button(按钮),ImageButton(图片按钮),ToggleButton(开关按钮)
ImageView(图片控件)
CheckBox(复选框控件),RadioButton(单选框控件)

4、布局


布局

是可用于放置很多控件的容器按照一定的规律调整内部控件的位置由此构成界面。



嵌套布局

布局内部放置布局,多层布局嵌套,可以完成复杂的界面结构



5、 ios 基础知识


布局

iOS 不使用布局的概念,用变量之间的相对关系完成位置的计算



注意

使用 Appium 测试 iOS 应用需要使用 MacOS 操作系统



6、 元素定位

概念:元素定位的含义就是定位控件
注意:同一脚本同时支持 android/iOS 两个系统的前提是元素属性(id,aid,xpath 等)一致

7、 控件基础知识


dom:Document Object Model 文档对象模型


dom 应用:用于表示界面的控件层级,界面的结构化描述

常见的格式:html、xml
核心元素:节点、属性



xpath:xml 路径语言,用于 xml 中的节点定位


Anrdroid 应用的层级结构与 html 不一样,是一个定制的 xml


app source 类似于 dom ,表示 app 的层级,代表了界面里面所有的控件树的结构


每个控件都有它的属性(resourceid,xpath,aid),但是没有 css 属性


8、 app dom 结构解析


node


attribute


clickable


content-desc


resource-id


text


bounds

 

9、 iOS 与 Android dom 结构的区别

  • dom 属性和节点结构类似

  • 名字和属性命名不同

    • android 的 resourceid 和 ios 的 name
    • android 的 content-desc 和 ios 的 accessibility-id

10、 定位方法

  • 测试步骤三要素

    • 定位、交互、断言
  • 定位方式:

    • id 定位
    • accessibilty_id 定位
    • xpath 定位
    • classname 定位(不推荐)

11、 App 定位方式

 

12、 App 定位方式进阶

 

 

 

13、 选择定位器通用原则

  • 与研发约定的属性优先

    • android 推荐 content-description
    • ios 推荐 label
  • 身份属性 id

  • 组合定位 xpath,css

  • 其它定位

14、 元素定位的写法

  • 返回单个元素 WebElement
  • 返回元素列表 [WebElement, WebElement, WebElement…]
# 返回单个元素 WebElement
driver.find_element(AppiumBy.xxx, "xxx属性值")
# 返回元素列表 [WebElement, WebElement, WebElement...]
driver.find_elements(AppiumBy.xxx, "xxx属性值")

driver.find_element(AppiumBy.ID, "ID属性值")
driver.find_element(AppiumBy.XPATH, "xpath表达式")
driver.find_element(AppiumBy.CLASS_NAME, "CLASS属性值")
driver.find_element(AppiumBy.ACCESSIBILITY_ID, "ACCESSIBILITY_ID表达式")
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, "android uiautomator 表达式")
driver.find_element(AppiumBy.IOS_UIAUTOMATION, "ios uiautomation 表达式")
driver.find_element(AppiumBy.ANDROID_VIEWTAG, "ESPRESSO viewtag 表达式")
driver.find_element(AppiumBy.ANDROID_DATA_MATCHER, "ESPRESSO data matcher 表达式")
driver.find_element(AppiumBy.IMAGE, "IMAGE图片")

15、 ID 定位

  • 通过身份标识 id 查找元素
  • 写法:find_element(AppiumBy.ID, "ID属性值")

16、ACCESSIBILITY_ID 定位

  • 通过 accessibility id 查找元素
  • 写法:find_element(AppiumBy.ACCESSIBILITY_ID, "ACCESSIBILITY_ID属性值")

17、 XPath 定位

 

18、 XPath 单属性定位

  • 基本表达式://*[@属性名='属性值']

19、 XPath 多属性定位

  • 表达式://*[@属性名='属性值' and @属性名='属性值' ]

实战练习

  • 安装 ApiDemo.apk - 链接: 百度网盘 密码: gdcw
  • 打开应用
  • 定位文字为【App】元素
class TestLocation:
    def setup(self):
        caps = {}
        caps["platformName"] = "Android"
        caps["appium:appPackage"] = "io.appium.android.apis"
        caps["appium:appActivity"] = ".ApiDemos"
        caps["appium:deviceName"] = "127.0.0.1:7555"
        caps["dontStopAppOnReset"] = "true"
        caps["noReset"] = "true"
        self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", caps)
        self.driver.implicitly_wait(5)

    def teardown(self):
        self.driver.quit()

    def test_id(self):
        """通过 ID 进行元素定位"""
        print(self.driver.find_element(AppiumBy.ID, "android:id/text1"))

    def test_aid(self):
        """通过 ACCESSIBILITY_ID 进行元素定位"""
        print(self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "App"))

    def test_xpath(self):
        """通过 XPATH 进行元素定位"""
        print(self.driver.find_element(AppiumBy.XPATH, "//*[@text='App']"))

    def test_xpath1(self):
        """通过 XPATH 进行元素定位"""
        print(self.driver.find_element(AppiumBy.XPATH, "//*[@text='App' and @resource-id='android:id/text1']"))

 

原生定位

  • 官网地址:UiSelector  |  Android Developers

20、 Android 原生定位

  • 元素属性定位
  • ID 定位
  • 文本定位
  • 文本匹配定位
  • 父子关系定位
  • 兄弟关系定位

21、 Android 原生定位 - 单属性定位

  • 格式 'new UiSelector().属性名("<属性值>")'

    • 比如:'new UiSelector().resourceId("android:id/text1")'
  • 注意外面是单引号,里面是双引号,顺序不能变

  • 可以简写为 属性名("<属性值>")'

    • 比如:·resourceId("android:id/text1")
# ID 定位
def test_android_uiautomator_by_id(self):   
        print(self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,\
                'new UiSelector().resourceId("android:id/text1")'))
# TEXT 定位
def test_android_uiautomator_by_text(self):
        print(self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,\
                'new UiSelector().text("App")'))

# classname 定位
def test_android_uiautomator_by_className(self):   
        print(self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, \
                'new UiSelector().className("android.widget.TextView")'))

22、 Android 原生定位-组合定位

  • 多个属性同时确定元素的(多个属性任意组合 ,不限长度)
driver.find_element_by_android_uiautomator('\
    new UiSelector().resourceId("com.xueqiu.android:id/tab_name").\
    text("我的")')

23、Android 原生定位-模糊匹配

  • 文字包含
  • 文字以 x 开头
  • 文字正则匹配
# 模糊匹配
def test_android_uiautomator_by_text_contains(self):
    print(self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().textContains("ssi")').text)

def test_android_uiautomator_by_text_start_with(self):
    print(self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().textStartsWith("Ani")').text)

def test_android_uiautomator_by_text_match(self):
    print(self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().textMatches("^Pre.*")').text)

24、 Android 原生定位-层级定位

  • 兄弟元素定位 fromParent
  • 父子结点定位 childSelector, 可以传入 resourceId() , description() 等方法
# 查找目标元素Text,先找App ,fromParent() 方法可以查找兄弟结点
new UiSelector().text("App").fromParent(text("Text"))

# 根据父结点查找子结点/ 子孙结点
new UiSelector().className("android.widget.ListView").childSelector(text("Text"))

25、 滑动查找元素

new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text("查找的元素文本").instance(0))

总结

  • Appium 提供多种元素定位方式,id,xpath, class, 也可以通过 Android Uiautomator 定位,或 iOS Predicate
  • xpath 是比较灵活的定位方式

六、强制等待与隐式等待

1、 添加等待的作用

  • 避免页面未渲染完成后操作,导致的报错

2、 直接等待

  • 解决方案:在报错的元素操作之前添加等待
  • 原理:强制等待,线程休眠一定时间
  • time.sleep(3)
from appium import webdriver
import time
desired_caps={}
desired_caps['platformName']='Android'
desired_caps['platformVersion']='6.0'
desired_caps['deviceName']='emulator-5554'
desired_caps['appPackage']='com.xueqiu.android'
desired_caps['appActivity']='com.xueqiu.android.common.MainActivity'
desired_caps['noReset'] = "true"
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub",desired_caps)
time.sleep(3)
driver.find_element(AppiumBy.ID, \
    "com.xueqiu.android:id/tv_search").click()
time.sleep(3)
driver.find_element(AppiumBy.ID, \
    "com.xueqiu.android:id/search_input_text").send_keys("alibaba")
    
driver.find_element(AppiumBy.ID,\
    "com.xueqiu.android:id/code").click()

driver.quit()

3、 隐式等待

  • 问题:难以确定元素加载的具体等待时间。
  • 解决方案:针对于寻找元素的这个动作,使用隐式等待添加配置。
  • 原理:隐式等待是一种全局的等待方式,设置一个等待时间,轮询查找(默认 0.5 秒)元素是否出现,如果没出现就抛出异常
#设置一个等待时间,轮询查找(默认0.5秒)元素是否出现,如果没出现就抛出异常
driver.implicitly_wait(3)

4、 隐式等待无法解决的问题

  • 元素可以找到,使用点击等操作,出现报错

  • 原因:

    • 页面元素加载是异步加载过程,通常 xml 会先加载完成,相应的元素属性后加载
    • 元素存在与否是由 xml 决定,元素的交互是由属性决定
    • 隐式等待只关注元素能不能找到,不关注元素能否点击或者进行其他的交互
  • 解决方案:使用显式等待

5、 显式等待基本使用(初级)

  • 示例: WebDriverWait(driver实例, 最长等待时间, 轮询时间).until(结束条件)
  • 原理:在最长等待时间内,轮询,是否满足结束条件
WebDriverWait(driver, 10).until(
        expected_conditions.element_to_be_clickable(
        (AppiumBy.ID, 'com.xueqiu.android:id/code')))

driver.find_element(AppiumBy.ID,"com.xueqiu.android:id/code").click()

总结

 

七、常见控件交互方法

1、元素的常用方法

  • 点击方法 element.click()

  • 输入操作 element.send_keys('appium')

  • 设置元素的值 element.set_value('appium')

  • 清除操作 element.clear()

  • 是否可见 element.is_displayed() 返回 True/False

  • 是否可用 element.is_enabled() 返回 True/False

  • 是否被选中 element.is_selected() 返回 True/False

  • 获取属性值 get_attribute(name)

    • get_attribute() 方法能获取的属性,元素的属性几乎都能获取到,属性名称和 uiautomatorviewer 里面的一致

    • 源码地址: github.com/appium/appi…

    • get_attribute() 可以获取的属性

      • resource-id/resourceld 返回 resource-id(API=>18 支持)
      • text 返回 text
      • class 返回 class(API=>18 支持)
      • content-desc/contentDescription 返回 content-desc 属性
      • checkable,checked,clickable,enabled,focusable,focused,{long-clickable,longClickable), package, password,scrollable,selection-start,selection-end,selected,bounds,displayed,contentSize 返回 true or false

2、 元素常用属性

  • 获取元素文本

    • 格式:element.text
  • 获取元素坐标

    • 格式:element.location
    • 结果:{'y': 19,'x: 498}
  • 获取元素尺寸(高和宽)

    • 格式:element.size
    • 结果:{'width':500,'height':22)
      def test_seeking(self):
              """
              打开 demo.apk
              1. 点击 Animation 进入下个页面
              2. 点击 Seeking 进入下个页面
              3. 查看【RUN】按钮是否显示/是否可点击
              4. 查看【滑动条】是否显示/是否可用/是否可点击
              5. 获取【滑动条】长度
              6. 点击【滑动条】中心位置
              :return:
              """
              self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "Animation").click()
              self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "Seeking").click()
              # 查看【RUN】按钮是否显示、是否可点击
              run_element = self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "Run")
              run_is_displayed = run_element.is_displayed()
              run_is_clickable = run_element.get_attribute("clickable")
              print(f"【run】按钮是否可见:{run_is_displayed},是否可点击:{run_is_clickable}")
              # 查看【滑动条】是否显示/是否可用/是否可点击
              seekbar_element = self.driver.find_element(AppiumBy.ID, "io.appium.android.apis:id/seekBar")
              seekbar_displayed = seekbar_element.is_displayed()
              seekbar_enabled = seekbar_element.is_enabled()
              seekbar_clickable = seekbar_element.get_attribute("clickable")
              print(f"seekbar 滑动条 是否可见:{seekbar_displayed},"
                    f"是否可用:{seekbar_enabled},"
                    f"是否可点击:{seekbar_clickable}")
              # 获取【滑动条】长度
              seekbar_size = seekbar_element.size
              width = seekbar_size.get("width")
              height = seekbar_size.get("height")
              print(f"seekbar 的长度:{width}")
      
              seekbar_location = seekbar_element.location
              x = seekbar_location.get("x")
              y = seekbar_location.get("y")
              # 点击【滑动条】中心位置
              seekbar_centerx = x + width / 2
              seekbar_centery = y
              self.driver.tap([(seekbar_centerx, seekbar_centery)])
              sleep(5)
      
      

示例练习

  • 打开 demo.apk
  • 点击 Animation 进入下个页面
  • 点击 Seeking 进入下个页面
  • 查看【RUN】按钮是否显示/是否可点击
  • 查看【滑动条】是否显示/是否可用/是否可点击
  • 获取【滑动条】长度
  • 点击【滑动条】中心位置

八、自动化测试定位策略

1、App 定位方式

2、App 定位方式进阶

 

3、 Web 定位方式

 

4、 选择定位器通用原则

  • 与研发约定的属性优先

    • web 推荐 class
    • android 推荐 content-description
    • ios 推荐 label
  • 身份属性 id,name(web 定位)

  • 组合定位 xpath,css

  • 其它定位

5、 元素定位不到的原因

6、混合定位的应用场景

  • 场景:

    • 属性动态变化(id,text)
    • 重复元素属性(id,text,class)
  • 解决:

    • 根据相对位置关系进行定位(css、xpath)(父级,子级,兄弟,索引)
    • 使用 find_elements 遍历查找
  • 参考高级定位技巧章节(xpath,css)

7、使用等待机制的场景

  • 场景

    • 控件动态出现
    • 控件出现特定特征
  • 解决

    • 元素定位结合隐式等待与显式等待

8、Web 弹框定位

  • 场景

    • web 页面 alert 弹框
  • 解决:

    • web 需要使用 driver.switchTo().alert() 处理

9、 App toast 提示框定位

  • 场景

    • app toast 提示框
  • 解决:

    • 使用 driver.page_source 拿到页面布局结构文件,分析 toast/弹框组件的标签内容,
    • 然后通过 id/text/class 等属性,使用 xpath 完成元素定位
    • 结合 隐式等待

10、 下拉框/日期控件定位

  • 场景:

    • <input>标签组合的下拉框无法定位
    • <input>标签组合的日期控件无法定位
  • 解决:

    • 面对这些元素,我们可以引入 JS 注入技术来解决问题。

11、 文件上传定位

  • 场景:

    • input 标签文件上传
  • 解决:

    • input 标签直接使用 send_keys()方法

最后: 为了回馈铁杆粉丝们,我给大家整理了完整的软件测试视频学习教程,朋友们如果需要可以自行免费领取 【保证100%免费】

 全套资料获取方式:点击下方小卡片自行领取即可

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/807238.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux系统中MySQL主从复制

本节主要学习了MySQL Replication概述&#xff0c;优点&#xff0c;复制类型&#xff0c;复制方式&#xff0c;复制过程和复制过程的限制&#xff0c;部署MySQL主从异步复制的流程及问题解决。 目录 一、MySQL Replication概述 1、优点 二、MySQL复制类型 1.异步复制&#…

#vue3报错 Cannot read properties of null (reading ‘isCE‘)#

场景&#xff1a;使用 npm 安装依赖包的时候&#xff0c;如如安装 npm i xlsx npm i file-saver 重新运行报错 Cannot read properties of null (reading isCE)# 解决办法&#xff1a; 使用的vite vue 在vite.config.ts添加如下配置&#xff1a; dedupe: [ vue ]

LeetCode使用最小花费爬楼梯(动态规划)

使用最小花费爬楼梯&#xff08;动态规划&#xff09; 题目描述算法流程(方法一)编程代码优化代码算法流程&#xff08;方法二&#xff09;编程代码代码优化 链接: 使用最小花费爬楼梯 题目描述 算法流程(方法一) 编程代码 class Solution { public:int minCostClimbingStair…

【雕爷学编程】Arduino动手做(104)---16X16点阵汉字屏模块2

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

8种工程设计优化问题Matlab代码:多种智能优化算法求解对比

目录 一、8种工程设计优化问题 二、8种工程设计优化问题部分问题介绍 2.1 焊接梁设计 2.2 拉力压缩弹簧设计 2.3 三杆衍架设计问题 2.4 压力容器设计 2.5 减速器设计问题 三、多种智能优化算法分别求解部分工程设计优化问题 3.1 焊接梁设计 3.2 拉力压缩弹簧设计 3.…

数据结构基础之二叉树

文章目录 二叉树性质二叉树分类遍历二叉树如何判断是否为完全二叉树 二叉树是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式&#xff0c;即使是一般的树也能简单地转换为二叉树&#xff0c;而且二叉树的存储结构及其算法都较为简单&#xff0c;因此…

企业选择租用CRM还是一次性买断CRM?分别有哪些优势?

CRM是企业管理客户关系&#xff0c;提升销售业绩&#xff0c;实现业务增长的重要工具。市场上的CRM系统销售方式主要有两种——租用型和买断型。那么&#xff0c;租用CRM好还是一次性买断CRM好&#xff1f;本文将从以下几个方面进行分析&#xff1a; 1、什么是租用型CRM和买断…

shell中按照特定字符分割字符串,并且在切分后的每段内容后加上特定字符(串),然后再用特定字符拼接起来

文件中的内容&#xff0c;可以这么写&#xff1a; awk -F, -v OFS, {for(i1;i<‌NF;i){$i$i"_suffix"}}1 input.txt-F,&#xff1a;设置输入字段分隔符为逗号&#xff08;,&#xff09;&#xff0c;这将使awk按照逗号分割输入文本。-v OFS‘,’&#xff1a;设置输…

数据结构:栈和队列的实现和图解二者相互实现

文章目录 写在前面栈什么是栈栈的实现 队列什么是队列队列的实现 用队列实现栈用栈模拟队列 写在前面 栈和队列的实现依托的是顺序表和链表&#xff0c;如果对顺序表和链表不清楚是很难真正理解栈和队列的 下面为顺序表和链表的实现和图解讲解 手撕图解顺序表 手撕图解单链表 …

VMware虚拟机中配置静态IP

目录 环境原因基础概念VMnet网络IPV4网络私有地址范围Vmnet8的作用网路通信的过程解决方法1&#xff1a;修改k8s组件重新启动解决方法2&#xff1a;配置静态IP系统网卡设置设置虚拟机网关修改虚拟机网卡 环境 本机系统&#xff1a;windows11虚拟机系统&#xff1a;CentOS-7-x8…

【AutoGluon_03】保存模型并调用模型

在训练好autogluon模型之后&#xff0c;可以将模型进行保存。之后当有新的数据需要使用autogluon进行预测的时候&#xff0c;就可以直接加载原来训练好的模型进行训练。 import pandas as pd from sklearn.model_selection import train_test_split from autogluon.tabular im…

第九章:stack类

系列文章目录 文章目录 系列文章目录前言stack的介绍stack的使用成员函数使用stack 总结 前言 stack是容器适配器&#xff0c;底层封装了STL容器。 stack的介绍 stack的文档介绍 stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除…

数字孪生技术从哪些方面为钢铁冶炼厂提高管理效率?

数字孪生系统是一种数字化技术&#xff0c;可以将物理世界中的实体对象、过程和数据进行数字化建模&#xff0c;以实现对其的可视化、模拟和优化。在炼铁生产管控中&#xff0c;数字孪生系统可以为以下方面提供支持&#xff1a; 炼铁生产线的可视化和控制&#xff1a;通过数字…

Web3 叙述交易所授权置换概念 编写transferFrom与approve函数

前文 Web3带着大家根据ERC-20文档编写自己的第一个代币solidity智能合约 中 我们通过ERC-20一种开发者设计的不成文规定 也将我们的代币开发的很像个样子了 我们打开 ERC-20文档 我们transfer后面的函数就是transferFrom 这个也是 一个账号 from 发送给另一个账号 to 数量 val…

指针初阶(1)

文章目录 目录1. 指针是什么2. 指针变量的类型2.1 指针变量-整数2.2 指针变量的解引用 3. 野指针3.1 野指针成因3.2 如何规避野指针 4. 指针运算4.1 指针-整数4.2 指针-指针4.3 指针的关系运算 附&#xff1a; 目录 指针是什么指针变量的类型野指针指针运算指针和数组二级指针…

redis集群设置

先下载redis数据库可以在一台机器上设置redis集群高可用 cd /etc/redis/ mkdir -p redis-cluster/redis600{1..6} for i in {1..6} do cp /opt/redis-5.0.7/redis.conf /etc/redis/redis-cluster/redis600$i cp /opt/redis-5.0.7/src/redis-cli /opt/redis-5.0.7/src/redis-s…

号外号外!首届开源 AI 游戏挑战赛圆满结束!

&#x1f917; 宝子们可以戳 阅读原文 查看文中所有的外部链接哟&#xff01; 北京时间 7 月 8 日到 7 月 10 日&#xff0c; 我们举办了首届开源 AI 游戏开发挑战赛。这是一场激动人心的赛事活动&#xff0c;游戏开发者在紧迫的 48 小时内使用 AI 创造、创新有创意的游戏。 本…

gazebo学习记录(杂乱)

一、完整系列教程 如何使用gazebo进行机器人仿真&#xff08;很重要&#xff09;&#xff1a;https://zhuanlan.zhihu.com/p/367796338 基础教程和关键概念讲解&#xff08;很重要&#xff09;&#xff1a;https://zhuanlan.zhihu.com/p/363385163 古月居&#xff1a;http://w…

Web自动化测试高级定位xpath

高级定位-xpath 目录 xpath 基本概念xpath 使用场景xpath 语法与实战 xpath基本概念 XPath 是一门在 XML 文档中查找信息的语言XPath 使用路径表达式在 XML 文档中进行导航XPath 的应用非常广泛XPath 可以应用在UI自动化测试 xpath 定位场景 web自动化测试app自动化测试 …

Selenium多浏览器处理

Python 版本 #导入依赖 import os from selenium import webdriverdef test_browser():#使用os模块的getenv方法来获取声明环境变量browserbrowser os.getenv("browser").lower()#判断browser的值if browser "headless":driver webdriver.PhantomJS()e…