自我介绍

智慧管网 - opensync plume,

我负责的接口测试模块

1 这个项目是一个类似于家庭网关的管理,通过web端控制硬件设备。当设备支持该模块时在接入公网时,在web端通过添加设别snid,会自动进行拓扑连接。我做的就是测试这个设备是否支持该项功能,以及功能完善情况。

我主要是负责封装api和调用api获取云信息和获取设备信息,进行一个效验模块,方向有登录上线,上线初始化后的一个同步,下发指令后api和设备信息同步问题

2 erp系统,主要是UI和接口方面的自动化。销售、采购、商品出入库等关联内容

3 这个项目是通过web自动化,模拟操作设备后台管理。通过以一些列特定操作,最后从中输出一份设备的xml格式配置。检验后台管理功能是否生效,这一直是一个维护项目。版本迭代需要配合系统测试人员一同出报告

智慧管网

这个项目是一个类似于家庭网关的管理,通过web端控制硬件设备。当设备支持该模块时在接入公网时,在web端通过添加设别snid,会自动进行拓扑连接。我做的就是测试这个设备是否支持该项功能,以及功能完善情况。

使用pipenv,和docker结合创建项目镜像和环境

项目使用pytest+jinkens+selenium+jsonPath+template+requests+allure+csv

我主要是负责

  • dockerfile其实就是一个类似于shell一样的脚本。把多个docker搭建的语法写在一个文件,dockerfile.base基础镜像,dockerfile.full 基于基础镜像的代码添加。主要是使用了from 设置基础镜像,run 执行脚本,env 设置环境变量等关键字

  • 整个项目配置文件的封装,主要是设备方面配置

    • 读取yaml,return一些文件内容
    • 使用pyyaml第三方库
      • yaml.load()yaml.safe_load(YAML字符串或文件句柄):yaml -> 字典,如yaml中有中文,需要使用 字符串.encode('utf-8')或打开文件时指定encoding='utf-8'
      • yaml.dump(字典):默认为flow流格式,即字典{b': {'c': 3, 'd': 4}},会被转为b: {c: 3, d: 4}形式,可以使用default_flow_style=False关闭流模式
  • 封装api和调用api获取云信息和获取设备信息,进行一个效验模块
    方向有登录上线,上线初始化后的一个同步,下发指令的api和设备信息同步问题,后期是同另一个同事完善用例代码

    • 这里有3个方向:

      1. 封装requests,各个请求方法,base_url;封装相关api。我们会把一个接口分成多个模块。每个模块放置相关联的url资源链接。在testcase上调用

      2. 封装数据效验模块

        • 通过创建类的方式,初始化项目api。将配置的环境变量和传递的接口链接进行拼接。请求相对应接口数据。使用jsonPath进行数据截取。再通过调用ssh类进行设别信息获取。然后通过封装好的效验模块进行效验

        • 通过创建diff类,里面定义了文本、数字、列表、字典等不同数据类型等多种效验方法,

      3. UI方面则是使用PO模型,把相关页面用代码的形式表现。底层basepage还是之前别的框架封装的

  • 通过重写了pytest hooks函数: pytest_addoption,添加了命令行额外参数也就是传入csv测试用例的地方。

    • 重写了pytest_addoption, 添加一个–list-csv的参数,传递一个csv数据,在pytest_collection_modifyitems 保留出csv中存在的case。在每条case执行完后,还会通过pytest_runtest_makereport钩子函数将结果写回result。之所以这样做,主要是因为,这个主要是项目有280多个case。但并非都是要执行的。根据某些需要添加的需求,执行某些case。通过csv存储的caseid进行测试执行
      • 为什么不用标记
        1. 直接给出csv,在后续需要查找相关的case时,可以在pdf测试步骤文档直接通过id查找,比较方便,可以脱离框架代码
        2. 灵活性高,可以应对各种不同需求,可能搞好这个标记的case,我只需要执行某一些
        3. 扩展性强,后期代码新需求维护时,不用计较分在那个模块。按需测试
  • 在测试登录case的时候,通过request.config.token = ‘xxx’将整个身份验证token存储在pytest config属性中;

  • 这是一个可视化项目,所以结合使用subprocess.call调用了airtest方法对图像进行定位

    1
    2
    if exists(Template(r"image.png", threshold=0.8)):  
    touch(Template(r"image.png"))

erp系统

企业管理系统,相当于后台管理,因为重构了项目的一些模块,所以自动化框架也要重新写。

主要是在web UI和功能还有结构数据,还有数据库操作上,前面那个项目接口和UI是合在一个框架里的。但是这个不一样,接口和UI框架式分开的两个框架,听说是因为先写的接口自动化测试,后续才加上的UI方面

重构了相关页面的定位属性,和操作方法

F5670系列 wifi 后台

主要是到了UI自动化和ssh 远程连接硬件设别系统获取相关数据以及该系列产品的系统测试,以系统测试为主自动化测试为辅的方式

appiume2和appium1有什么区别

  1. 架构重构:Appium 2 重构整体架构,进行了模块化划分和可扩展,提高了代码的可维护性和可拓展性。
  2. 移除 Selenium 依赖:Appium 2 不再依赖 Selenium 库,而是直接使用 WebDriver,这简化了代码依赖和提高了性能。
  3. 命令行支持:Appium 2 提供更强大的命令行工具,方便地管理测试任务和执行测试案例。
  4. 插件支持:Appium 2 支持插件功能,可以通过插件扩展 Appium 的功能,为用户提供更多选项。
  5. 性能提升:Appium 2 在性能方面做了一些优化,提高了测试执行的效率和速度。
  6. 支持新特性:Appium 2 更新了一些 API 和功能,支持一些新的移动应用测试特性。
  7. appium2支持了图像识别功能,可以和airtest一样通过图像的方式定位元素

接口自动化

说一下接口自动化是怎么做的 && 说说你们做自动化的流程

一般项目会有一个需求分析会议。然后就是写测试计划和以及自动化可行性分析,自动化框架选型等。根据需求和设计做测试用例设计,初步编写框架内容,在用例评审完后,进行进再一步完善框架和用例,并将需要自动化测试的用例,使用代码的形式表现出来。把自动化流程化,测试环境通过测试后,输出框架文档和用例测试步骤文档。使用git提交代码。使用git options和jinkens进行持续集成,后续就是维护了

框架具体如何实现的

公司的测试框架基本是基于pytest测试框架基础上编写

还使用到了pytest,allure,jsonpath,requests等技术栈,基于api模式的封装。将接口的请求和响应封装在一个对象中,并为每个接口创建一个对应的 API Object 类,以及相应的方法来执行请求和处理响应。

并且还重写了pytest hocks 函数和pytest插件,扩展了框架其他功能
最后生成allure报告,通过jinkens进行持续集成

公司内部测试流程(手动测试)通常包括以下步骤:

  1. 需求分析:测试团队与业务团队共同分析项目需求,确定需要测试的功能模块和测试重点。
  2. 测试计划:测试团队编写测试计划,明确测试范围、测试目标、测试资源和时间安排等。
  3. 测试用例设计:根据需求和设计文档,编写测试用例,包括测试输入、预期输出和测试步骤等。
  4. 环境搭建:准备测试环境,包括硬件、软件、数据库等,并确保测试环境与生产环境一致。
  5. 执行测试用例:测试人员按照测试计划执行测试用例,手工测试系统的功能、界面、性能等。
  6. 缺陷管理:发现缺陷后,测试人员编写缺陷报告,包括缺陷描述、复现步骤和优先级等,并将缺陷分配给开发团队进行修复。
  7. 重复执行:对已修复的缺陷进行验证和回归测试,确保问题已经解决,并系统稳定。
  8. 测试报告:测试团队编写测试报告,包括测试结果、缺陷汇总、测试覆盖率和建议等,汇报给相关团队。

自动化测试流程则包括以下步骤:

  1. 选型和设计:选择适合的自动化测试工具和框架,并设计自动化测试的架构和框架。
  2. 编写测试脚本:根据测试需求和设计,编写自动化测试脚本,包括测试用例和断言。
  3. 环境配置:配置自动化测试执行环境,包括构建、部署和设置自动化测试环境。
  4. 执行测试脚本:执行自动化测试脚本,自动化运行测试用例,并生成测试报告和日志。
  5. 监控和分析:监控测试执行情况,分析测试结果和异常情况,并生成分析报告。
  6. 集成自动化测试:将自动化测试集成到持续集成/持续交付流程中,实现自动化测试的持续执行。
  7. 维护和优化:维护自动化测试脚本,持续优化测试框架和流程,提高自动化测试的效率和稳定性。

为什么要做自动化

通过自动化测试,可以提高测试效率、减少人为错误,加快软件开发迭代速度,提高软件质量

自动化测试有什么缺点

  1. 前期需要时间、资源和技术投资,不适合生命周期比较短的项目
  2. 如果需求变化频繁,也不太适合
  3. 操作死板,目标太明确,无法像功能测试一样,发现其他存在的bug,类似探索性测试

做自动化的过程中遇到过什么问题,怎么解决的

之前遇到过那种嵌套了flash的一种业务流程展示的动画前端页面,这种在webdriver中并没有定位的详细方法和使用内容。
结合了pywinauto的自动化鼠标点击的方式操作,但有很多缺点,不能跨平台。使用这个的原因,第一,公司大部分自动化还是在window上面跑的,除非有一些对电脑性能有需求的可能会换成linux。第二,就是如果不使用pywinauto,就只能使用图像识别的方式。但flash已经不维护了且被现代浏览器基本上启用了。

用例测试失败,你如何处理,失败的日志你是如何处理的

一般自动化是24小时运行的,如果有fail的话,fail失败的信息会贴在allure或者其他报告的结果上,我们只需要看测试结果的报告通过入参和截图去分析就好; 一般日志都是控制台输出并且输出在一个文件夹内,方便后续查看。还在pytest_runtest_makereport pytest hooks重写,当fail时触发屏幕截图,将截图贴在报告上

https://store.steampowered.com/app/219740/_/

接口用例设计

一般使用等价类,边界值,场景法来设计用例

注重方向为:业务逻辑和入参

入参:

  • 设计:正例,反例,请求方式,参数设计
    • 反例:
      • 鉴权反例:token,key,为空,错误,过期
      • 参数反例:必填效验,长度规则,类型规则
      • 错误码覆盖
      • 其他:分页,图片格式,大小尺寸,上传类型

一个接口你会设计多少个测试用例

正常来说会有10几个,但如果接口应用复杂会有甚至20个左右

甚至还有多个接口进行结合测试

自动化覆盖率达到了多少

我们基本上实现了85%以上的接口用例覆盖率。

  • 接口自动化本身可执行性会比UI以及其他自动化要高很多
  • 自动化测试用例其实就是从功能测试用例里面挑出来的
  • 并不是所有都支持做自动化,有些操作需要人机交互验证,临时活动功能,不是长期的。这类不会去做自动化

如何评估和监控自动化测试的覆盖率和质量?

  • 测试报告分析
  • 数据监控工具
  • 错误分析
  • 持续集成工具
  • 代码覆盖率工具分析

碰到过框架的难点

这个框架的难点在于没有灵活的处理数据判断。
封装了很多判断方法,通过关键子的方式,在excel中会给出一个关键字值,在代码层面封装关键字的函数方法。通过不同关键字调用不同方法。后期还可以拓展这些关键字。(有json_data,判断某一个json值,或者json_text判断某个字段是否在json中。以及mysql数据库数据判断。以及文件上传下载的文件完整性和正确性判断)

你在自动化测试中遇到过的最大挑战是什么?你是如何解决的?

自动化测试中的挑战可能包括维护脚本的稳定性、处理复杂的测试环境配置、处理动态页面和异步操作等。解决这些挑战的方法可以包括规范的脚本编写、适当的等待机制、模拟测试环境和使用持续集成。

自动化测试给项目组带来的效益是什么

提升效率,冒烟,回归测试。特别是版本递归比较频繁的项目。

怎么保证框架的稳定性

  • 做完善的错误处理,保持代码的健壮性
  • 尽量减少依赖性耦合性
  • 定期维护和更新
  • 做详细的日志记录

接口自动化做大什么颗粒度,接口自动化执行通过率

一般来说可以做到:

  • 单元测试:针对单个接口进行测试,验证其返回结果是否符合预期
  • 集成测试:针对多个接口进行测试,验证接口间关联和交互是否符合预期
  • 系统测试:针对整个系统的业务业务流程进行测试,验证所有接口是否能协同工作,是否满足业务需求

自动化覆盖率:85%
自动化通过率:99%

接口自动化出发点 / 谈谈您对自动化测试的理解和经验。

自动化测试是利用自动化工具和脚本来执行测试用例和验证软件功能,提高测试效率和覆盖率。自动化测试可以提高测试速度、减少测试人力成本、提高测试稳定性和可重复性,适合重复性较高的测试任务。

接口关联怎么处理

postman和jmeter处理接口关联

环境变量,json提取器,正则提取器,cooike提取器

测试框架

方法一:
通过单独yaml文件来保存中间提取值,这些值会在所有测试用例之前清空
unittest:setup_class
pytest:conftest+fixtrue
直接添加在整个pytest全局属性的config中

方法二:
进行全局变量存储,在所有测试用例之前清空

接口自动化测试框架封装

basepage基础封装,testcase封装,requests二次封装,日志收集模块,yaml,csv等文件读写封装。以及一些数据加工处理

使用数据驱动进行测试

当对同一个测试用例,进行不同数据进行测试。
@pytest.mark.parameterize(参数化装饰器),可以在测试用例中进行多组数据进行参数化进行数据驱动测试
unittest 可以使用第三方库 ddt 进行数据参数化

unittest和pytest区别

  1. 断言方法
    • pytest 内置支持丰富的断言方法,如 assert, assertEqual, assertIn 等。而 unittest 的断言方法相对较少,需要使用 unittest.TestCase 类中的断言方法。
  2. 自动发现测试用例
    • pytest 具有自动发现测试用例功能,只需按照指定的命名规则编写测试文件和测试方法,并运行 pytest 命令即可自动识别并执行测试用例。而 unittest 需要手动编写测试套件和加载测试用例。且测试用例类需要继承unittest.TestCase
  3. 插件支持
    • pytest 支持丰富的插件系统,可以通过插件来扩展其功能和特性。这使得 pytest 更加灵活和可定制化。相比之下,unittest 的扩展性较为有限。
  4. 参数化测试
    • pytest 内置支持参数化测试,可以通过装饰器 @pytest.mark.parametrize 实现一次编写多组测试数据并执行多次测试的功能。而 unittest 需要通过子类化 unittest.TestCase 类来实现参数化测试。
  5. 异常处理
    • pytest 提供了更好的异常处理机制,可以更友好地展示测试失败的详细信息和上下文。相比之下,unittest 的异常处理相对简单。

举例说明接口测试碰到过的那些异常

  1. 网络连接异常
    • 当请求无法成功连接到目标服务器时,例如网络故障或目标服务器不可用,可能会出现的异常。例如,ConnectExceptionSocketTimeoutException
  2. 4xx 客户端错误(例如:404、401、403等):
    • 当请求的资源未找到(404)、未授权(401)、禁止访问(403)等时,HTTP响应状态码会反映出这些错误。
  3. 5xx 服务器错误(例如:500、502、503等):
    • 当服务器遇到内部错误(500)或服务不可用(503)等时,会返回相应的HTTP状态码。
  4. **超时异常 (TimeoutException)**:
    • 当请求超出预定的时间限制时,通常会引发此异常。这可能是由于服务器响应缓慢或网络问题导致的。
  5. **请求格式错误 (HttpClientErrorException)**:
    • 当请求参数或请求体的格式不符合API要求时,可能会抛出此异常。
  6. JSON 解析异常
    • 在解析服务器返回的JSON响应时,如果响应体不是有效的JSON格式,可能会导致解析错误(如 JsonParseException)。
  7. SSL/TLS 相关异常
    • 在使用HTTPS协议时,如果SSL证书验证失败,可能会抛出与SSL/TLS相关的异常,例如 SSLHandshakeException
  8. 连接池异常
    • 如果使用连接池管理HTTP连接,连接池耗尽或配置错误时可能会引发异常。
  9. 响应内容不符异常
    • API返回的响应内容与预期不符时,可以自定义异常类型,以帮助识别不合规的响应。
  10. 未处理异常
    • 在编写代码时,如果某些异常没有被捕获并处理,可能会导致程序崩溃。这类异常应通过适当的异常处理机制进行管理,例如 Exception 类。

怎么测试数据的准确性

  1. 数据验证:通过数据验证技术来验证数据的准确性,包括数据的唯一性、完整性、一致性和有效性。对于不同类型的数据,可以使用不同的验证技术,如格式验证、范围验证、规则验证等。
  2. 对比测试:将生成的数据与预期的数据进行对比,验证数据的内容、格式、字段是否一致。可以使用对比工具或脚本来比较两个数据集,并查找差异。
  3. 边界测试:测试数据的边界值,确保数据在限定范围内正确处理。通过输入数据的最大值、最小值、临界值等测试数据的边界条件。
  4. 随机测试:利用随机数据生成算法生成大量随机数据,并对数据进行验证。随机测试可以帮助发现数据处理中的潜在问题和异常情况。

Jmeter+Ant

使用 Ant 运行 JMeter 测试

  1. 创建 Ant 构建文件

首先,创建一个 build.xml 文件,在此文件中定义 Ant 任务来执行 JMeter 测试。以下是一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<project name="JMeterTest" default="run-jmeter" basedir=".">  
<!-- 设置 JMeter 和结果输出目录 -->
<property name="jmeter.home" value="/path/to/jmeter"/>
<property name="jmeter.script" value="path/to/your/test_plan.jmx"/>
<property name="jmeter.result.file" value="results.jtl"/>
<property name="jmeter.log.file" value="jmeter.log"/>

<!-- 任务: 运行 JMeter 测试 -->
<target name="run-jmeter">
<exec executable="${jmeter.home}/bin/jmeter" failonerror="true">
<arg value="-n"/> <!-- 非 GUI 模式 -->
<arg value="-t"/>
<arg path="${jmeter.script}"/> <!-- 指定测试计划 -->
<arg value="-l"/>
<arg path="${jmeter.result.file}"/> <!-- 指定结果日志 -->
<arg value="-j"/>
<arg path="${jmeter.log.file}"/> <!-- 指定日志文件 -->
</exec>
</target>
</project>

说明

  • jmeter.home:定义 JMeter 的安装目录。
  • jmeter.script:指定测试计划的路径。
  • jmeter.result.file:定义测试结果输出的 .jtl 文件。
  • jmeter.log.file:定义 JMeter 日志文件的路径。
  • run-jmeter 任务使用 <exec> 标签来运行 JMeter。
  1. 在 Jenkins 中配置构建任务
  2. 创建新任务
    • 登录 Jenkins,点击“新建任务”。
    • 选择“自由风格项目”,然后输入项目名称。
  3. 配置构建环境
    • 在“构建环境”部分,选择“添加构建步骤”并选择“Invoke Ant”。
    • 在“目标”字段中,输入 run-jmeter(与 Ant 构建文件中的目标名称相同)。
  4. 配置源码管理(可选):
    • 如果需要从版本控制系统(如 Git)获取代码,可以在“源码管理”中配置相关选项。
  5. 设置构建触发器(可选):
    • 在“构建触发器”中,你可以设置定期构建、基于 git push 触发等。
  6. 构建后操作
    • 你可以配置“构建后操作”,例如发送邮件通知、生成报告等。
  7. 运行 Jenkins 任务
  • 点击“立即构建”,Jenkins 将按照你配置的步骤执行 Ant 构建,运行 JMeter 测试。测试结果将在 Jenkins 的控制台输出中显示。

结果报告

查看 JMeter 结果

    • JMeter 生成的结果文件(results.jtl)可用于后续分析和报告。
    • 可以使用 JMeter 自带的 HTML 报告生成工具,生成可视化报告。
  1. 生成 HTML 报告(可选):

    • 可以在 Ant 构建文件中添加一个新的任务,在 JMeter 完成测试后生成报告:
    1
    2
    3
    4
    5
    6
    7
    8
    <target name="generate-report" depends="run-jmeter">  
    <exec executable="${jmeter.home}/bin/jmeter" failonerror="true">
    <arg value="-g"/>
    <arg value="${jmeter.result.file}"/>
    <arg value="-o"/>
    <arg value="report"/> <!-- 指定报告输出目录 -->
    </exec>
    </target>

ui自动化

selenium,appium工作原理

Selenium:

  1. Selenium 是一个用于Web应用程序测试的自动化测试工具,支持多种编程语言(如Java、Python、C#等)。
  2. Selenium 包含了一组工具和API,主要有三种组件:Selenium IDE、Selenium WebDriver 和 Selenium Grid。
  3. Selenium WebDriver 是最主要的组件,它通过驱动浏览器来执行测试用例,实现模拟用户操作的自动化。
  4. Selenium WebDriver 将测试脚本和浏览器之间建立一个通信桥接,接收命令执行操作,并将结果返回给测试脚本。
  5. Selenium WebDriver 支持各种浏览器,通过不同的浏览器驱动程序(如ChromeDriver、GeckoDriver等)来与浏览器进行交互。

Appium:

  1. Appium 是一个开源的自动化测试框架,用来测试原生移动应用、混合应用和移动网页应用。
  2. Appium 是跨平台的,支持iOS和Android平台,可以使用相同的API和脚本语言来编写测试用例。
  3. Appium 的工作原理类似于Selenium WebDriver,它通过模拟用户操作来自动化执行测试用例。
  4. Appium 通过启动应用程序的测试会话来控制应用程序,发送各种命令来模拟用户在移动设备上的操作。
  5. Appium 使用UIAutomator、XCUITest 等框架来与Android和iOS应用进行通信,并可以使用不同的测试客户端来编写测试脚本。

Selenium中有哪些定位方式

8种

  • tag
  • 三大基本属性 id/name/class_name
  • 链接 link_text/partial_link_text
  • 高级 css_selector/xpath

弹框怎么处理

3种弹框

  • div弹框、dom弹框,遮罩框 这些前端研发自己封装的直接定位就可以
  • alert框 / propmt提示框 浏览器自带弹窗需要通过 dr.swich_to.alert().dismiss() / .accept()
  • BasicAuth授权框 dr.get(“http://用户名:密码@www.example.com“)

Selenium中有几种等待

  • 隐形等待/智能等待 dr.implicitly_wait()
  • 显性等待 WebDriverWait - 也叫条件等待,只针对某个元素设定,也可以结合js使用
    • 常用的等待条件
      • presence_of_element_located(locator):等待页面中至少一个元素出现。
      • visibility_of_element_located(locator):等待元素在页面中可见。
      • element_to_be_clickable(locator):等待元素可点击。
      • text_to_be_present_in_element(locator, text):等待特定元素中出现特定的文本。
      • title_contains(title):等待页面标题包含特定文本。
  • 强制等待 time.sleep()
  • 使用场景
    • 复杂的用户交互流程:等待某个按钮变为可点击状态,然后执行点击操作。
    • 动态加载的内容:等待某个异步加载的区块出现,然后验证其内容。
    • 表单提交和响应:等待页面提交后的特定结果或反馈信息。

在自动化测试中,你如何处理动态页面和异步加载的元素?

动态页面和异步加载的元素可以使用等待机制来处理。等待机制可以通过显式等待或隐式等待来实现,等待直到元素出现或满足特定条件,再继续执行下一步操作。

文件上传和文件下载

  • 文件上传

    • send_keys()
    1
    2
    3
    4
    # 定位上传文件的input元素  
    upload_input = dr.find_element_by_xpath("//input[@type='file']")
    # 输入文件路径
    upload_input.send_keys(r"C:\path\to\file.txt")
    • 如果网页禁止send_keys(),可以使用js修改元素属性方式进行上传
    1
    2
    3
    4
    # 打开需要上传文件的网页  
    dr.get("http://www.example.com/upload")
    # 直接输入文件路径(使用JavaScript)
    dr.execute_script("document.querySelector('input[type=file]').value = 'C:\\path\\to\\file.txt';")
  • 文件下载

    • 通过点击按钮或者链接
    • Selenium可能无法直接实现文件上传或下载,可以考虑借助第三方工具来实现。例如,使用AutoIt、Sikuli等工具来模拟键盘操作或屏幕操作来处理文件上传和下载

怎么截图

  • 整个浏览器屏幕截图

    1
    driver.save_screenshot('screenshot.png')  
  • 元素内容截图

    1
    2
    element = driver.find_element_by_css_selector('.example-element')  
    element.screenshot('element_screenshot.png')

定位不到元素如何处理

  • 添加等待处理
  • 观察页面 特殊的操作,比如页面跳转,元素出现隐藏,弹出alert框
  • 元素可能在iframe框架里,需要用特定方法处理
  • 研发修改了元素属性,需要时常维护自动化框架
  • 定位的属性是动态的

如何应对页面元素变动问题

可以使用PO模型框架设计,将单个页面的属性放到相应模块内,方便维护

selenium遇到flash怎么解决

使用pywinauto或其他GUI自动化工具生成exe,配合selenium执行

如何去定位页面上动态元素

  • 属性动态

    • 尽量选用不是动态的属性定位

    • 使用文本匹配或者模糊匹配

      • 使用部分文本匹配:可以通过XPath或CSS选择器来使用部分文本匹配元素。

        • 使用XPath:

          1
          element = driver.find_element_by_xpath("//*[contains(text(), '部分文本')]")  
        • 使用CSS选择器:

          1
          element = driver.find_element_by_css_selector("div:contains('部分文本')")  
      • 模糊匹配属性值:在定位元素时,可以使用正则表达式或通配符来匹配元素的属性值。

        • 使用正则表达式:

          1
          element = driver.find_element_by_xpath("//input[contains(@id, 'partialId')]")  
        • 使用通配符(*):

          1
          element = driver.find_element_by_xpath("//input[starts-with(@id, 'partialId')]")  
      • 组合多个条件进行匹配:可以结合多个条件进行匹配元素,以增加匹配的精准度。

        • 使用and:

          1
          element = driver.find_element_by_xpath("//input[contains(@id, 'partialId') and @class='classValue']")  
        • 使用or:

          1
          element = driver.find_element_by_xpath("//input[contains(@id, 'partialId') or @class='classValue']") 
  • 元素在页面上是动态的,例如nav导航栏隐藏,菜单栏收缩(看如何定位隐藏元素)

selenium 是否支持C/S模式

Selenium是一套浏览器自动化测试框架,支持B/S模式的应用,C/S模式仅支持部分已浏览器为核心的应用,一般情况下不支持C/S模式。

如何定位页面上的隐藏元素

只能定位html上有的只是在浏览器页面没有显示

  1. 使用JavaScript定位:通过执行JavaScript代码来获取隐藏元素,即使元素在页面上隐藏也可以找到。
1
element = driver.execute_script("return document.getElementById('hiddenElement')")  
  1. 使用find_element方法:有时隐藏元素可能只是通过CSS样式隐藏,可以通过find_element方法来定位。
  2. 修改样式来显示元素:有时可以通过执行JavaScript代码来修改元素的CSS样式,将隐藏元素显示出来后再进行定位。
1
2
driver.execute_script("document.getElementById('hiddenElement').style.display = 'block';")  
element = driver.find_element_by_id('hiddenElement')

你的框架是如何设计的

  • 执行控制
  • 测试套件
  • 用例
  • 业务流 页面对象组合,常用的业务流程
  • PageObject 页面对象、页面基础类
  • 实用方法 数据读取 数据库操作 日志 发邮件 自己封装的定位方法

测试用例间具有依赖处理怎么解决

  • 确保执行顺序
  • 如果使用了并发编程,并发时以用例类suite为单位运行

举例说明碰到过的那些异常

UI自动化

  1. **元素未找到异常 (NoSuchElementException)**:
    • 当尝试查找一个在当前页面上不存在的元素时,会抛出此异常。可能是因为元素的定位器选择错误,或者页面尚未加载完毕。
  2. **元素不可交互异常 (ElementNotInteractableException)**:
    • 尝试与一个不可交互的元素(例如,被隐藏或未启用的元素)进行操作时,会抛出此异常。
  3. **超时异常 (TimeoutException)**:
    • 在等待某个条件(例如元素可见、可点击等)时超时,导致抛出此异常。可能是因为页面加载速度较慢,或者元素条件从未满足。
  4. **元素过时异常 (StaleElementReferenceException)**:
    • 当一个已找到的元素在DOM树中被改变后,再次尝试与该元素进行交互时,会抛出此异常。此时需要重新定位元素。
  5. **JavaScript错误 (WebDriverException)**:
    • 在执行JavaScript时,如果页面出现错误,可能会导致WebDriver无法正常工作,从而抛出此异常。
  6. **无法切换上下文异常 (NoSuchFrameException / NoSuchWindowException)**:
    • 尝试切换到一个不存在的框架或窗口时,会抛出此异常。
  7. **网络错误和页面未加载 (WebDriverTimeoutException)**:
    • 网络问题或页面未能加载完全可能导致WebDriver在进行操作时超时。
  8. **操作系统异常 (InterruptedException)**:
    • 当线程被中断时会抛出此异常,通常与多线程相关。
  9. **Assert异常 (AssertionError)**:
    • 在进行断言时,如果条件不符合预期,则会抛出此异常。
  10. **URL异常 (MalformedURLException)**:
    • 在尝试打开无效的URL时可能会抛出此异常。

如何提高脚本的稳定性

  1. 良好的错误处理
  2. 尽量少使用sleep强制等待
  3. 良好封装的元素定位方法和日志处理

webdriver出现异常怎么处理,怎么恢复场景和记录结果

  • 记录日志
  • 记录截图
  • 缓存上次执行结果

垃圾数据怎么处理

可以在用例夹具方法中清除

移动端自动化( app,h5,小程序)

app测试具体怎么做

  1. 首先做功能测试,保证功能,UI,布局过关
  2. 专项测试, 安装,卸载,兼容性,稳定性,弱网,性能

App兼容性

App兼容性主要是在不同机型,不同系统版本,不同分辨率下做测试,像是否正常显示,是否有拉伸,显示不全,或者白屏效果

app更新测试

  1. 强制更新
    • 强制更新功能是否成功,功能是否正常
    • 更新后数据是否正常
    • 强制升级弹窗是否可以关闭
    • 强制更新提示,包括已更新,未更新
    • 版本号对比
  2. 非强制更新
    • 提示弹框的显示,是否可以选择暂不更新和立即更新;是否可以关闭
    • 暂不更新,旧版本是否还能使用
    • 立即更新后,更新进程是否正常
    • app版本更新是否能手动查询最新版本,进行更新
    • 版本号对比

App功能失效,如何排查客户端还是服务端

  1. 检查是否网络问题,查看其他app是否能正常使用
  2. 检查版本,更换其他手机品牌或操作系统尝试是否能解决问题
  3. 抓包分析

App闪退如何排查

  1. 问题重现,记录步骤和现象
  2. 联合开发查看崩溃日志,详细分析

web和app测试区别

web是B/S app是C/S,架构不同,所以web端一般服务器更新时,客户端不需要更新,app如果服务端更新,就需要同步app的数据,将app进行更新。所以app需要一些其他的专项测试。像安装,卸载,兼容性,稳定性,弱网等

abd命令

  • adb start-server
  • adb kill-server
  • adb devices
  • adb -s 设备 id pull 设备路径
  • adb logcat
  • adb connect
  • adb shell

测开

讲一下对测开岗位的理解

测开是一种集软件测试与开发于一身的工作岗位,主要负责设计和编写自动化测试脚本、进行系统性能测试、负载测试、接口测试、UI测试等工作,旨在保证软件质量和稳定性。

需要具备扎实的编程基础和软件测试知识,熟练掌握至少一种编程语言和自动化测试框架,对测试工具和技术有深入的了解和实践经验。此外,还需要具备良好的沟通能力和团队合作精神,能够与开发人员、产品经理等密切合作,共同推动软件开发过程中的质量保障工作。

对于项目的质量建设你做了那些工作

  1. 明确质量标准进行
  2. 指定质量管理计划
  3. 设计和实施自动化测试框架
  4. 编写测试用例
  5. 执行不同类型的测试
  6. 分析测试结果
  7. 定期检查和评估测试流程维护
  8. 培训团队成员

自动化用例的执行策略是什么

本质与软件开发一致,经过测试需求分析,设计出自动化测试用例,搭建自动化测试框架,设计编写自动话脚本,验证脚本,最终完成自动化测试脚本,并输出测试结果,自动化测试框架,一般多用在版本迭代里面使用

自动化测试最大的缺陷是什么

不稳定,没有办法做到手工测试那种能够应对突发情况,根据项目越大,维护可能比较难

高质量的自动化测试脚本应具备以下特点:

  1. 稳定性:脚本应能在不同环境下稳定运行,不受外部因素影响。
  2. 完善的错误处理:
  3. 日志记录
  4. 精确的报告输出
  5. 易维护

如何提升自动化测试用例的稳定性

1)编写健壮的测试脚本,确保其能处理不同的输入和环境变化;

2)使用合适的等待策略(如显式等待)以避免因页面加载延迟导致的测试失败;

3)定期维护和更新测试用例,以应对应用程序的变化;

4)实施隔离测试环境,确保测试不会因外部因素受影响。

如何最大限度保证设计用例的覆盖度

1)识别所有的用户需求和功能场景;

2)创建详尽的用例文档,涵盖各种用户交互和系统反应;

3)进行全面的用例评审,以确保每个功能点都有相应的用例;

4)使用用例跟踪矩阵来验证所有需求是否都被覆盖;

5)定期更新用例以反映需求变化。

自动化脚本编写过程中,遇到过哪些问题,怎么解决的

  1. 脚本稳定性问题 - 错误处理,错误捕获
  2. 环境构建问题 - 一般出现在docker,比如说docker镜像构建dockerfile,需要时长维护,因为开源脚本维护是有会影响。或者发布了新版本,旧版本会放在旧版本页面
  3. 接口返回数据对比,需要封装一个文件,以及字符串,字典格式对比

数据库

mysql

数据库操作

image-20240923152704335

image-20240923152725649

image-20240923152737449

image-20240923152749156

image-20240923152759421

image-20240923152811769

image-20240923152826732

image-20240923152841139

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 创建数据库
CREATE DATABASE new_database;
# 查看所有数据库名
show DATABASES;
# 使用数据库
use <database_name>; # database_name 为数据库名字
# 数据库数据导入导出
$ mysqldump -u username -p old_database > dump.sql
$ mysql -u username -p new_database < dump.sql
# 删除数据库
DROP DATABASE old_database;
# 修改数据库命名
RENAME DATABASE old_database TO new_database;
# 复制数据库或复制数据库内容
CREATE TABLE new_database.table_name LIKE old_database.table_name;
INSERT INTO new_database.table_name SELECT * FROM old_database.table_name;
表操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 创建表
CREATE TABLE table_name (
id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
age INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
# 修改表字段数据类型
ALTER TABLE table_name
MODIFY column_name new_data_type;
# 添加新字段
ALTER TABLE table_name
ADD column_name data_type;
# 修改字段名
ALTER TABLE table_name
CHANGE old_column_name new_column_name data_type;
# 删除字段
ALTER TABLE table_name
DROP column_name;
# 修改字段属性
ALTER TABLE table_name
MODIFY column_name new_data_type modification;
# 删除表
DROP TABLE table_name;
# 修改表名
ALTER TABLE old_table_name RENAME TO new_table_name;
表数据操作
1
2
3
4
5
6
7
8
# 查询表数据
SELECT * FROM table_name;
# 插入数据
INSERT INTO table_name (column1, column2, ...) VALUES (value1, value2, ...);
# 更新表数据
UPDATE table_name SET column1 = value1, column2 = value2 WHERE condition;
# 删除表数据
DELETE FROM table_name WHERE condition;
数据查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 查询所有数据
SELECT * FROM table_name;
# 查询结果返回指定字段或者命名
SELECT column1 as '1列', column2 as '2列' FROM table_name;
# 条件查询
SELECT * FROM users WHERE age > 30;
# 查询数据排序
SELECT * FROM table_name ORDER BY column_name ASC|DESC;
# 使用 LIMIT 子句限制返回行数:
SELECT * FROM table_name LIMIT offset, count;
# 聚合查询,对相同数据进行分组
SELECT column1, SUM(column2) FROM table_name GROUP BY column1;
# 多表查询 - 内连接
SELECT column1, column2
FROM table1
INNER JOIN table2 ON table1.column = table2.column;
# 多表查询 - 左连接
SELECT column1, column2
FROM table1
LEFT JOIN table2 ON table1.column = table2.column;
# 多表查询 - 右连接
SELECT column1, column2
FROM table1
RIGHT JOIN table2 ON table1.column = table2.column;
# 子查询
SELECT column1, (SELECT MAX(column2) FROM table2) AS max_value
FROM table1;
mysql和redis使用场景,区别

MySQL:

使用场景:MySQL是一种关系型数据库管理系统,适用于需要保证数据完整性和一致性的应用场景,如电子商务、金融系统、社交网络等。

Redis:

使用场景:Redis是一种内存中的数据结构存储系统,适用于需要高性能读写和快速响应的应用场景,如缓存、会话管理、排行榜等。

区别:

  1. 数据存储:MySQL数据存储在磁盘上,Redis数据存储在内存中。
  2. 数据结构:MySQL适用于结构化数据存储和查询,Redis支持多种类型的非结构化数据。
  3. 响应速度:Redis读写速度更快,适合高性能读写的场景;MySQL数据读取和写入相对较慢。
  4. 数据一致性:MySQL支持ACID事务,保证数据一致性和完整性;Redis是基于内存的,可能存在数据丢失的风险。
sql慢查询排查
  1. 启用慢查询日志来记录
  2. 使用EXPLAIN关键字可以分析查询语句的执行计划
  3. 检查索引
  4. 分析慢查询的SQL语句和表结构
  5. 使用缓存
  6. 使用性能分析工具
mysql有哪些索引类型,原理

MySQL支持多种类型的索引,常见的索引类型包括:

  1. B-Tree索引:B-Tree(平衡树)索引是MySQL中最常用的索引类型。它适用于等值查询、范围查询和排序操作。B-Tree索引适用于大多数场景,包括单列索引、组合索引和唯一索引。
  2. 哈希索引:哈希索引适用于等值查询,但不支持范围查询和排序操作。哈希索引将索引列的值通过哈希函数映射到一个哈希表中,可以快速定位到具体的数据行。但是,哈希索引不适用于范围查询和模糊查询。
  3. 全文索引:全文索引用于全文搜索,适用于对文本内容进行关键字搜索的场景。全文索引可以提供更高级的搜索功能,如关键字匹配、模糊搜索和排序。
  4. 空间索引:空间索引用于处理地理空间数据,如地理位置坐标。它支持空间数据类型和空间函数,可以进行空间范围查询和距离计算。
  5. 前缀索引:前缀索引是指只对索引列的前缀部分进行索引,而不是整个列。它可以减少索引的存储空间,但可能会影响查询的性能。
  6. 其他特殊索引:MySQL还支持其他一些特殊类型的索引,如全文空间索引、JSON索引等,用于特定的数据类型和查询需求。

MySQL索引的原理如下:

  1. 创建索引:当在表的列上创建索引时,MySQL会根据索引类型(如B-Tree或哈希)创建相应的数据结构,并将索引列的值和对应的指针存储在索引结构中。
  2. 查询优化器:当执行查询语句时,MySQL的查询优化器会分析查询语句和表的结构,决定使用哪个索引来执行查询。优化器会考虑索引的选择性、列的顺序、查询条件等因素,选择最优的索引。
  3. 索引扫描:当使用索引执行查询时,MySQL会根据索引的数据结构进行索引扫描。对于B-Tree索引,MySQL会根据查询条件的范围进行二分查找,定位到目标数据行。对于哈希索引,MySQL会通过哈希函数计算,直接定位到目标数据行。
  4. 数据访问:一旦定位到目标数据行,MySQL会使用指针获取对应的数据,并返回给查询结果。
索引底层原理

素引是数起库中的一种数起结构,用于提高对数据的查找效率,常见的索引数据结构包括B+例、哈希素引等。

MySQL索引是如何支持百万级别查询的

MySQL索引是基于B+树的,B+树是类似与跳表的一种数据结构,查询效率为log(N)

数据库事务具有ACID这4个特性
  • A:Atomicity,原子性,将所有SQL作为原子工作单元执行,要么全部执行,要么全部不执行;
  • C:Consistency,一致性,事务完成后,所有数据的状态都是一致的,即A账户只要减去了100,B账户则必定加上了100;
  • I:Isolation,隔离性,如果有多个事务并发执行,每个事务作出的修改必须与其他事务隔离;
  • D:Durability,持久性,即事务完成后,对数据库数据的修改被持久化存储。
存储过程特点
  • 存储过程是一段被编译、存储在数据库中并可供重复使用的SQL代码块。
  • 存储过程可以接受参数作为输入和输出。
  • 存储过程可以包含流程控制语句、循环语句、条件语句等,使得复杂的逻辑操作可以在数据库层面完成。
  • 存储过程可以提高数据库的性能,减少网络通信开销,并减少客户端与数据库之间的交互次数。

redis

redis数据类型及使用场景
  • 字符串,列表,哈希,集合,有序集合
  1. 缓存:作为缓存存储热点数据,减轻数据库压力,提高访问速度。
  2. 会话存储:保存用户会话信息,实现状态共享,方便用户跨服务器访问。
  3. 计数器:记录网站访问量、点赞数等数据,支持高并发访问。
  4. 分布式锁:实现分布式系统中的锁机制,确保数据的一致性和并发操作的安全性。
  5. 消息队列:通过Redis的列表数据结构实现简单的消息队列,支持消息的发布与订阅。
  6. 实时数据分析:存储实时更新的数据,如实时地图数据、活跃用户数等。
  7. 地理位置服务:利用Redis的地理位置数据结构,实现地理位置的存储和检索。
  8. 实时排行榜:实现用户、文章、产品等实时排行榜,根据分数排名。
  9. 缓存逻辑结果:存储逻辑计算结果,避免重复计算,提高系统性能。
  10. 分布式限流:结合Redis的计数器特性,实现接口访问频率限制,防止过多请求导致系统崩溃。
Redis持久化

Redis 中提供了两种持久化方式:RDB 快照(Snapshotting)和 AOF 日志(Append-only file)。

  1. RDB 快照
    • RDB 快照是将 Redis 在某个时间点上的数据状态保存到磁盘上的一个数据文件中。
    • Redis 会周期性地将内存中的数据快照保存到磁盘中,生成一个快照文件。
    • RDB 快照是一个紧凑且可读的二进制文件,一般用于备份和恢复操作。
    • RDB 持久化配置:
      • save <seconds> <changes>:指定在 N 秒内有 M 个写操作时进行 RDB 快照持久化。
      • stop-writes-on-bgsave-error yes/no:在进行 RDB 持久化时,如果写操作失败是否停止写入。
      • rdbcompression yes/no:是否对 RDB 文件进行压缩。
  2. AOF 日志
    • AOF 日志是将 Redis 所有写操作追加到一个文件中,在服务器重启时会重新执行这些写操作以恢复数据。
    • AOF 文件包含了 Redis 服务器执行的所有写命令,在服务器重启时可以从 AOF 文件恢复数据。
    • AOF 日志通常以文本形式保存,易于阅读和检查。
    • AOF 持久化配置:
      • appendonly yes:开启 AOF 日志持久化。
      • appendfsync always/everysec/no:指定何时将写入操作同步到磁盘。
      • auto-aof-rewrite-percentage <percentage>:在 AOF 文件大小超过之前大小的一定百分比时,启动 AOF 重写。
redis的key大小设置多少合理

Redis的key大小设应该根据实际需求来定,一酸来说,建议使用有意义及统一格式的key,而不是过长的key,因为过长的key会影响内存占用及数据查性能

什么是缓存雪崩、缓存击穿及缓存穿透?如何应对?
  • 缓存雪崩:大面积Key失效导致数据库压力剧增,应对方法
    • Key失效时间设置随机值,防止同时失效
    • 缓存指向分布式数据库,以分担数据库压力
    • 热点数据设置为用不过期
  • 缓存击穿:缓存无,数据库有。高并发访问下缓存失效导致数据库压力剧增,应对方法
    • 热点数据设置为用不过期
    • 使用互斥锁对数据库访问限流
  • 缓存穿透:缓存无,数据库无。如非法访问。
    • 对接口增加校验,拦截非法数据
    • 对无值对Key缓存null值
Redis的应用场景
  1. 缓存-热数据
  2. 计数器incrby
  3. 队列
  4. 位操作 setbit getbit bitcount
  5. 分布式锁与单线程机制 秒杀
  6. 最新列表 list
  7. 排行榜 zset

网络原理

http协议包括哪些部分

1)请求消息,包括请求行(如请求方法、URL、协议版本)和请求头;
2)响应消息,包括状态行(如状态码、状态描述)和响应头;
3)消息体,用于传输实际的数据内容;
4)HTTP 状态码,用于表示请求的结果

  • 200 OK:请求成功,服务器成功返回请求的数据。
  • 201 Created:请求成功,服务器成功创建了新的资源。
  • 204 No Content:请求成功,服务器成功处理了请求,但没有返回任何内容。
  • 301 Moved Permanently:请求的资源已永久移动到新的 URL。
  • 400 Bad Request:请求无效,服务器无法理解请求的语法。
  • 401 Unauthorized:请求要求身份验证,客户端未提供有效的身份验证凭据。
  • 500 Internal Server Error:服务器在执行请求时发生了未知的内部错误。
  • 501 Not Implemented:服务器不支持实现请求所需要的功能,或者无法完成请求。
  • 502 Bad Gateway:作为网关或代理工作的服务器尝试执行请求时,从上游服务器接收到无效响应。

登录一个网址,这个时候服务器做了什么动作

  1. 接收请求
  2. 验证凭据
  3. 处理认证
  4. 设置 Cookie
  5. 响应请求
  6. 记录日志
  7. 维持会话管理

TCP 和 UDP 的区别及应用场景。

  1. 可靠性
    • TCP 是一种可靠的协议,它提供了数据包的确认和重传机制,以确保数据的可靠传输。它使用序列号、确认和超时重传等机制来保证数据的准确性和完整性。
    • UDP 是一种不可靠的协议,它不提供数据包的确认和重传机制。数据的传输没有确认机制,可能会丢失、重复或者无序。
  2. 连接性
    • TCP 是面向连接的协议,通过三次握手建立连接,并保持双方的连接状态。在数据传输完成后,通过四次挥手断开连接。
    • UDP 是无连接的协议,它不需要建立和维护连接。每个数据包都是独立的,可以独立地发送或接收。
  3. 传输效率
    • TCP 在传输可靠性方面提供了很好的保证,但由于引入了确认和重传机制,以及连接的建立和断开过程,会增加一些开销,导致传输效率相对较低。
    • UDP 没有确认和重传机制,以及连接建立过程,传输效率相对更高。数据包的发送和接收速度更快。
  4. 应用场景
    • TCP 适用于对数据传输可靠性要求较高的应用场景,如文件传输、电子邮件、网页浏览等。它可以确保数据的完整性和顺序,但传输效率相对较低。
    • UDP 适用于对实时性要求较高、数据传输可靠性要求相对较低的应用场景,如音视频流媒体、在线游戏、网络电话等。它传输效率高,但对数据的可靠性要求不高。

http,https,rpc的区别,和使用上的区别

HTTP、HTTPS和RPC是三种不同的协议,它们在网络通信中有着不同的作用和特点:

HTTP:HTTP是一种应用层协议,使用80端口,不加密数据传输,HTTP通常用于在浏览器和服务器之间传输超文本数据,例如网页、图片、视频等。它通常用于Web应用程序开发、网站浏览等场景。

HTTPS:HTTPS是HTTP的安全版本,使用使用HTTP+SSL / TLS协议,使用443端口,会传输过程中对数据进行加密,用于保护数据在传输过程中的安全性。HTTPS常用于敏感数据传输,如个人信息、银行交易等

RPC:RPC是一种远程过程调用协议,用于在不同计算机之间实现远程过程调用。通常用于构建分布式系统和微服务架构。RPC允许不同服务之间进行远程通信,以实现功能拆分和解耦,提高系统的可扩展性和性能。

  • Sun RPC:默认端口为 111。
  • Microsoft RPC:默认端口为 135。
  • XML-RPC:默认端口为 80。

https加密过程(SSL/ TLS)

  1. 客户端请求建立安全连接
  2. 服务器响应并验证身份
  3. 客户端生成密钥
  4. 服务器解密密钥
  5. 双方生成会话密钥
  6. 数据传输加密

浏览器缓存 - 本地缓存。cookie,localStorage,sessionstorage,会把数据存在哪,受不受同源策略制约

  • Cookie:cookie是一种在客户端存储数据的机制。可以通过在HTTP响应头中设置Set-Cookie头部来创建和修改Cookie,下次请求信息的时候会自动在请求头中包含Cookie。Cookie有一些限制,例如每个响应的Cookie数量和总大小都有限制,同时Cookie会在每次请求时都被发送到服务器,会增加网络流量。Cookie 受同源策略的限制,只能被设置和访问与其所属网站相同的域名、协议和端口。
  • localStorage:是HTML5新增的一种在客户端存储数据的机制,数据以键值对的形式存储在客户端的浏览器中。数据会永久保存在浏览器的本地存储空间中,除非被显式清除或网站使用代码进行删除。每个域名有独立的 localStorage 存储空间,受同源策略的限制。
  • sessionStorage:sessionStorage 也是以键值对的形式将数据存储在客户端的浏览器中。与 localStorage 不同的是,sessionStorage 中的数据在数据只在当前会话(当前的浏览器窗口或者选项卡)有效。结束后会被清除,即当用户关闭浏览器标签页或窗口时会话结束。每个域名有独立的 sessionStorage 存储空间,受同源策略的限制。

cookie,session,token是保持用户会话状态的一个认证机制,因为HTTP 协议是一种无状态的协议,每个请求都是独立的。cookie会受同源策略的限制。session,token不受同源策略的限制。

  • Cookie:Cookie 是一种在客户端存储数据的技术,通常是由服务器发送到客户端的小型文本文件,以键值对的形式存在。通过HTTP响应头中的Set-Cookie字段传递给客户端,存储在客户端的浏览器中。Cookie 可以包含各种信息,如用户身份标识、会话状态,当前页面的状态,域名,过期时间等,在后续的请求中,浏览器会自动携带相应的Cookie信息发送到服务器。
  • Session:Session 是一种在服务器端存储用户会话数据的机制。当用户访问网站时,服务器会为用户创建一个唯一的会话标识符(Session ID),并将该标识符存储在服务器上,由服务器进行管理。客户端通常通过 Cookie 中的 Session ID 来与服务器建立关联,以便在后续的请求中识别用户会话。Session 数据不受同源策略的限制。
  • Token:Token 是一种用于验证用户身份和授权访问的令牌。它通常是由服务器生成的一串字符,可以包含用户身份信息和权限等数据。作为客户端和服务器之间进行身份验证和授权的凭据。Token 不依赖于服务器端的存储,而是通过在客户端和服务器之间传递进行验证。可以作为请求头的一部分发送到服务器。Token 可以用于实现无状态的身份验证和授权机制,不受同源策略的限制。
  • Name:Cookie 的名称,用于标识特定的 Cookie。
  • Value:Cookie 的值,存储在客户端的浏览器中,并在每次 HTTP 请求中被发送到服务器。
  • Domain:指定 Cookie 可以发送到哪些域名。默认情况下,Cookie 只能发送到设置它的页面所在的域名。可以通过设置 Domain 属性来扩展 Cookie 的作用域。
  • Path:指定 Cookie 可以发送到哪些路径。默认情况下,Cookie 只能发送到设置它的页面所在的路径。可以通过设置 Path 属性来限制 Cookie 的作用范围。
  • Expires/Max-Age:指定 Cookie 的过期时间。可以通过设置 Expires 属性为一个具体的日期时间或 Max-Age 属性为一个相对时间(秒数)来控制 Cookie 的有效期。

JWT - token

原理

服务器认证完成以后,会生成一个JSON对象,发挥给用户。之后用户与服务器通信时,都会发回JSON对象。服务器完全只靠这个对象认证用户身份。为了防止数据篡改,服务器会在生成JSON对象时,加上签名。

结构

token是一个很长的字符串,中间用点 (.) 分隔成三部分分别为 ( 头部-Header.负载-Payload.签名-Signature)。内部是没有换行的

认证流程
  1. 浏览器发起请求登录,用户携带用户名和密码等信息
  2. 服务器验证身份,根据算法,将用户标识符打包生成token
  3. 服务器返回JWT信息给浏览器
  4. 浏览器发起请求获取用户资料,会带上刚刚拿到的token
  5. 服务器数据发现有携带token,会进行身份验证
  6. 身份验证通过会返回该用户资料
特点
  • JWT默认是不加密的
  • JWT也可以用于信息交换,降低服务器查询数据库的次数
  • 最大缺点是,服务器不保存session状态,导致使用过程中,JWT一旦签发,在有效期内都能够使用,所以一旦泄露,任何人都可以获得该令牌的所有权限。除非加了其他逻辑判断。或者将有效期设置短一些
token无感刷新
  • token无感刷新,是在用户已经登录后,获取token时,在该token快过期时,自动调用后端,后端身份验证成功后,返回新token
    通过这种方式,可以在不打扰用户的情况下,自动刷新 Token,并确保用户的登录状态保持有效。

  • 如果是app应用,可以 使用推送通知 或 **使用后台服务(Android 应用)**或 定时任务的方式进行无感刷新

Websocket 相对于传统 HTTP 请求有哪些优势?

  1. 实时性:Websocket 提供了双向通信通道,使服务器可以主动向客户端推送数据,而不需要等待客户端发起请求。这样就可以实现实时数据传输,适用于需要实时更新的应用场景,比如聊天应用、实时数据监控等。
  2. 较低的通信开销:传统的 HTTP 请求每次通信都需要建立连接、传输请求头,而 Websocket 在初始连接建立后,可以保持长连接,减少了每次通信的开销,只需轻量级的数据帧来传输数据。
  3. 更少的网络延迟:由于 Websocket 建立了长连接,避免了重复建立和关闭连接造成的网络延迟,使得通信更加高效。这对于实时交互和快速响应的应用更为重要。
  4. 跨域通信:Websocket 建立在 TCP 协议上,不受同源策略的限制,可以轻松实现跨域通信,而传统的 HTTP 请求就受同源策略的限制。
  5. 更灵活的数据格式:Websocket 可以传输任意格式的数据,包括二进制数据,而传统的 HTTP 请求主要是基于文本的格式。

Websocket 建立连接的握手过程。

  1. 客户端发送一个 HTTP GET 请求给服务器
    包含特殊的 Upgrade 和 Connection 头部,以请求升级到 WebSocket 协议。请求示例:
1
2
3
4
5
6
GET /chat HTTP/1.1  
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

其中:

  • Upgrade 头部指定协议升级到 WebSocket。
  • Connection 头部表示希望使用长连接。
  • Sec-WebSocket-Key 是一个随机生成的密钥,用于安全验证。
  1. 服务器收到请求后进行处理
  • 验证客户端的请求合法性。例如,验证 Upgrade 头部是否为 websocket,Sec-WebSocket-Version 是否支持服务器的版本等。
  • 生成一个握手响应。响应示例:
1
2
3
4
HTTP/1.1 101 Switching Protocols  
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

其中:

  • Status Code 为 101 表示切换协议成功。
  • Sec-WebSocket-Accept 是服务器使用客户端传递的 Sec-WebSocket-Key 计算得到的响应密钥。
  1. 客户端收到服务器的响应后进行处理
  • 验证服务器响应的合法性。比对 Sec-WebSocket-Accept 是否与客户端请求中的 Sec-WebSocket-Key 计算得到的结果匹配等。
  • 若验证通过,握手成功,协议升级为 WebSocket,并开始进行双向数据传输。

get和post区别

  1. 数据传输方式:
    • GET:通过 URL 参数传递数据,数据附在 URL 后面,以 ? 开始,参数之间以 & 分隔。例如:http://example.com/path?name=value&age=30
    • POST:通过请求体传递数据,在 HTTP 请求的消息体中发送,并不会显示在 URL 中。
  2. 安全性:
    • GET:因为参数附在 URL 上,所以可能会被保存在浏览器历史记录、代理服务器日志等地方,不适合发送敏感信息。
    • POST:由于数据不会暴露在 URL 上,在一定程度上比 GET 更安全,并且可以使用 HTTPS 进行加密传输。
  3. 数据长度限制:
    • GET 请求对URL长度有限制(约 2KB),而 POST 没有明确的长度限制(取决于服务器和客户端设置)。
  4. 幂等性:
    • GET 请求通常是幂等的。即多次相同的请求会产生相同结果并且没有副作用。
    • POST 请求通常是非幂等的。即多次相同的请求可能每次产生不同结果或者有副作用(如创建一个新资源)。
  5. 缓存处理:
    • 对于相同URL和参数GET会被缓存到浏览器缓存或者CDN
    • POST则将忽略来自服务器指令,刷新会导致重复提交
  6. 长度
    • URL长度限制为4K/主要验证数据的准确性,包括分页
    • post在请求体中,内容没有限制

OSI 七层模型

描述计算机网络通信的标准框架,定义了网络通信系统中不同层次之间的互操作性。

  1. 物理层(Physical Layer):
    • 功能:负责传输比特流,定义传输介质的物理连接和电子特性。
    • 示例:网线、光缆、网卡等。
  2. 数据链路层(Data Link Layer):
    • 功能:实现节点之间的直接数据链接,处理物理层传输错误,并确保数据可靠传输。
    • 示例:以太网协议(Ethernet)、MAC 地址。
  3. 网络层(Network Layer):
    • 功能:处理数据包在网络中的路由和转发,实现不同网络之间的通信。
    • 示例:IP 协议、路由器。
  4. 传输层(Transport Layer):
    • 功能:提供端到端的数据传输服务,负责数据的分段和重新组装。
    • 示例:TCP 协议、UDP 协议。
  5. 会话层(Session Layer):
    • 功能:建立、管理和终止会话连接,可以在不同应用之间建立会话。
    • 示例:RPC、NetBIOS,SSL。
  6. 表示层(Presentation Layer):
    • 功能:处理数据格式、加密和压缩等操作,确保数据在网络中正确解释。
    • 示例:ASCII 编码、JPEG 图像压缩。
  7. 应用层(Application Layer):
    • 功能:为用户提供网络服务,实现各种应用程序之间的通信。
    • 示例:HTTP,FTP,SMTP,SSH。

TCP 和 UDP 的区别

  • TCP(Transmission Control Protocol)

    • 是面向连接的可靠传输协议,提供传输控制和数据可靠性保证。
    • 资源上和时间上耗费大
    • 传输的数据会保证可靠性和顺序保证,有重传机制
    • 使用流模式
  • UDP(User Datagram Protocol)

    • 是无连接的传输协议,不保证数据的可靠传输,但传输效率高。
    • 资源上和时间上耗费小
    • 只传输数据,不保证可靠性和完整习惯
    • 使用数据报模式

TCP 的三次握手,四次挥手过程

  • 三次握手 请求连接 -> 服务端确认 -> 客户端确认

    1. 客户端向服务器端发送 SYN 报文段,等待服务器确认。
    2. 服务器收到 SYN 报文段后,回复一个 SYN+ACK 报文段给客户端,表示确认请求。
    3. 客户端再向服务器端发送一个 ACK 报文段,表示连接确认,双方建立连接
  • 四次挥手 请求断开 -> 服务端同意 -> 服务端释放连接 -> 客户端确认

    1. 户端向服务器端发送连接释放请求,发送 FIN 报文段。
    2. 服务器端收到 FIN 后,回复 ACK 确认,并进入关闭等待状态。
    3. 服务器端也想断开连接,发送 FIN 报文段给客户端。
    4. 客户端收到 FIN 后,回复一个 ACK 确认,双方完成连接的关闭

场景问题

淘宝买东西页面白屏,怎么排查

  1. 检查网络问题
  2. 清除浏览器缓存
  3. 使用其他浏览器
  4. 用开发者调试工具初步检查请求相应问题,检查页面返回情况。如果没有返回页面,检查代理服务器执行情况,控制台日志
  5. 如果没问题,调用其他后端接口,检查后端运行情况,查看后端运行日志
  6. 最暴力的简单的就是,使用相同版本在测试环境进行测试,debug

出现线上问题,排查思路

  1. 通知团队

    • 在排查问题的同时,及时通知相关团队和负责人,协作解决问题。
  2. 确认问题现象

    • 首先要明确线上出现了什么问题,比如网站无法访问、功能异常、响应速度慢等。尽量详细描述问题的表现和影响。
  3. 查看日志

    • 查看应用程序、服务器和数据库的日志文件,并检查是否有异常信息或报错。日志文件通常是排查问题的重要线索。
  4. 监控系统

    • 查看监控系统,包括CPU、内存、磁盘、网络等性能指标,以及应用程序的错误率、请求量等情况,了解系统的实时状态。
  5. 版本控制

    • 检查近期是否有代码更改或系统配置调整,并分析是否和问题出现的时间点有关联。
  6. 网络连接

    • 检查网络连接是否正常,排查网络延迟、丢包等问题,尤其是对于分布式系统或者云服务。
  7. 逐层排查

    • 逐层排查系统的各个组件,从前端、应用层、数据库、缓存等依次检查,找出可能引起问题的组件。
  8. 复现问题

    • 尝试在开发环境或测试环境中复现问题,有助于找到问题的根本原因,并验证解决方案是否有效。
  9. 访问量和负载

    • 查看访问量是否异常增加,是否有特定的高峰时段,是否负载过高导致服务无法响应。
  10. 数据库查询

    • 对数据库进行性能分析,检查是否有慢查询、索引缺失等问题。
  11. 系统崩溃和异常处理

    • 查看系统是否存在内存泄漏、进程崩溃等异常情况,及时采取措施处理。

扫码支付场景测试case

  1. 正常支付流程
    • 手机扫描商家提供的支付二维码,输入支付密码或指纹,完成支付流程。
    • 确认支付成功页面展示正确信息。
  2. 支付码扫描逻辑
    • 测试支付码格式正确性,例如长度、字符集等。
    • 测试扫描支付码后解析正确性,包括商家信息、金额等。
  3. 超时处理
    • 测试支付码有效时间限制,验证支付是否在有效时间内完成。
    • 测试在有效时间内未完成支付,超时后的处理流程。
  4. 网络异常
    • 模拟网络异常情况,如断网、延迟等,验证支付流程是否正常处理。
  5. 支付失败处理
    • 测试支付密码错误、余额不足等情况下的支付失败处理流程,包括错误提示、返回上一步等。
  6. 退款流程
    • 测试退款流程,验证用户发起退款后的处理逻辑,退款金额是否正确到账。
  7. 多次支付/多次扫码
    • 测试多次扫码支付情况下,支付订单编号、支付金额等是否正确。
  8. 安全性测试
    • 验证支付过程中的交易安全性,如数据加密、防篡改等机制是否有效。
  9. 并发支付
    • 模拟多用户同时进行支付操作,验证系统能否正确处理同时支付请求。
  10. 支付成功通知
    • 验证支付成功后商家和用户收到支付成功通知的准确性和及时性。
  11. 支付记录查询
    • 测试用户可以查询历史支付记录,包括支付成功、失败记录等。
  12. 兼容性测试
    • 测试支付过程在不同设备、操作系统、浏览器等环境下的兼容性。

优惠券场景测试用例设计

  1. 正常使用优惠券
    • 用户选择商品后,输入有效的优惠券代码,验证优惠券是否成功应用到订单中,金额是否正确。
  2. 无效优惠券处理
    • 测试输入无效的优惠券代码或过期的优惠券代码,验证系统是否能正确提示无效优惠券或者过期优惠券。
  3. 重复使用优惠券
    • 验证同一优惠券是否可以多次使用,或者同一订单是否可以多次使用不同优惠券。
  4. 叠加优惠券
    • 测试用户是否可以同时使用多个优惠券,验证系统能否正确计算优惠金额。
  5. 优惠券适用范围
    • 验证优惠券的适用范围,包括指定商品、指定金额条件等是否被正确执行。
  6. 购物车结算
    • 测试在购物车结算页面输入优惠券代码,验证优惠券是否成功应用到购物车中的商品。
  7. 平台交易和商家自有优惠券
    • 在平台交易下验证优惠券的核销流程,以及商家自有优惠券在商家店铺中的使用流程。
  8. 优惠券有效期
    • 验证用户输入过期优惠券代码的处理逻辑,包括优惠券过期提醒或无法使用。
  9. 优惠券结算价计算
    • 验证系统在应用优惠券后,商品实际结算价格是否正确计算,考虑商品价格、优惠金额等因素。
  10. 优惠券退款处理
    • 测试使用优惠券后产生的订单进行退款操作,验证退款金额是否正确处理,优惠券是否正确返还。
  11. 性能测试
    • 在高并发场景下使用优惠券,测试系统的稳定性和性能。
  12. 安全性测试
    • 验证优惠券使用过程中的安全性,如数据加密、防止篡改等机制是否有效。

砍价功能,从测试角度分析

  1. 砍价流程

    • 发起砍价:验证用户是否能够成功发起砍价请求,并且能够选择合适的砍价目标和金额。
    • 砍价操作:测试用户是否能够成功完成砍价操作(如成功减少价格)。
    • 结果展示:确保砍价结果(如最终价格)正确显示,并且符合砍价规则。
  2. 砍价规则

    • 验证砍价规则是否按照预期执行,如每个用户的砍价次数限制、砍价金额范围等。
  3. 邀请和分享

    • 邀请功能:测试用户是否能够邀请好友参与砍价,并且邀请过程是否顺畅。
    • 分享功能:验证分享砍价活动到朋友圈或其他平台的功能是否正常工作。
  4. 价格调整

    • 确保价格的调整和展示在用户界面上是实时和准确的,砍价的历史记录是否准确。
  5. 并发测试

    • 多用户同时进行砍价操作,测试系统处理并发请求的能力,确保数据一致性和准确性。
  6. 设备和浏览器兼容性

    • 测试砍价功能在不同设备(如手机、平板)和浏览器上的兼容性。
  7. 网络环境

    • 在不同网络条件下测试砍价功能的稳定性和响应速度。
  8. 界面设计

    • 砍价页面的布局和操作流程是否用户友好,操作是否直观易懂。
  9. 操作反馈

    • 砍价过程中的反馈是否及时,如价格调整、邀请结果等。
  10. 速度和流畅性

    • 砍价操作的响应速度是否符合用户预期,保证操作过程流畅。

抽奖场景测试用例设计

  1. 正常抽奖流程
    • 用户进行抽奖操作,验证系统能够正确随机抽取奖品并展示给用户。
  2. 抽奖次数限制
    • 测试用户在规定时间内的抽奖次数限制,验证系统是否正确限制用户的抽奖次数。
  3. 奖品显示
    • 确认抽奖后展示的奖品信息、奖品图片等是否正确。
  4. 概率控制
    • 验证系统配置的抽奖概率是否正确,即不同奖品的抽中概率是否符合设定。
  5. 奖品发放
    • 测试抽中奖品后,验证系统能否正确发放奖品,包括实物奖品和虚拟奖品。
  6. 未中奖处理
    • 测试用户未抽中奖品的情况下,系统的提示信息和用户操作流程。
  7. 重复抽奖
    • 验证用户是否可以多次抽同一个奖品或不同奖品,系统的处理逻辑是否正确。
  8. 网络异常处理
    • 模拟网络异常情况下用户进行抽奖操作,验证系统的网络异常处理机制是否有效。
  9. 抽奖记录查询
    • 测试用户可以查询抽奖记录,包括中奖记录、未中奖记录等。
  10. 中奖通知
    • 验证用户抽中奖品后能否及时收到中奖通知,通知内容是否准确。
  11. 安全性测试
    • 验证抽奖过程中的安全性,如防止恶意程序干扰抽奖等措施是否有效。
  12. 性能测试
    • 在高并发情况下进行抽奖,验证系统的稳定性和性能。

朋友圈场景测试用例设计

  1. 发布文字动态
    • 测试用户是否能成功发布只包含文字内容的朋友圈动态,验证文字内容是否显示正确。
  2. 发布图片动态
    • 测试用户是否能成功发布包含图片的朋友圈动态,验证图片是否能正常显示。
  3. 发布视频动态
    • 测试用户是否能成功发布包含视频的朋友圈动态,验证视频播放是否正常。
  4. 发布链接动态
    • 测试用户是否能成功发布包含链接的朋友圈动态,验证链接是否可以正常跳转。
  5. 评论功能
    • 测试用户是否能成功对朋友圈动态发表评论,验证评论内容是否正确显示。
  6. 点赞功能
    • 测试用户是否能成功对朋友圈动态点赞,验证点赞数量是否正确显示。
  7. 分享功能
    • 测试用户是否能成功分享朋友圈动态到其他平台,验证分享链接的有效性。
  8. 删除动态
    • 测试用户是否能成功删除自己发布的朋友圈动态,验证动态是否被正常移除。
  9. 举报功能
    • 测试用户是否能成功举报其他用户发布的不良信息或违规内容,验证举报功能的有效性。
  10. 好友圈隐私保护
    • 验证用户设置朋友圈隐私后,他人是否无法查看其动态,包括设置只对特定好友可见等选项。
  11. 动态内容包含敏感词汇
    • 测试用户发布包含敏感词汇的动态,验证系统是否能正确识别并做出相应处理。
  12. 性能测试
    • 在高并发情况下测试朋友圈功能,验证系统的稳定性和性能。

性能测试指标?常用性能测试工具?

hps,tps,qps,rps,rt,并发用户数

  1. **HPS (Hits Per Second)**:每秒钟接收到的请求总数。衡量系统处理请求的能力。
  2. **TPS (Transactions Per Second)**:每秒处理的事务数量。事务可以是数据库操作、支付请求等,衡量系统处理事务的能力。
  3. **QPS (Queries Per Second)**:每秒处理的查询请求数量。常用于数据库或搜索引擎的性能衡量。
  4. **RPS (Requests Per Second)**:每秒接收的请求数量。通常用于衡量 Web 服务或 API 的处理能力。
  5. **RT (Response Time)**:请求到响应的时间间隔。衡量系统响应请求的速度。
  6. 并发用户数:同时在线或使用系统的用户数量。用于衡量系统在多用户环境下的承载能力。

linux常用命令

文件和目录操作

  • ls: 列出文件和目录。

    • la -a: 查看所有文件包括隐藏文件
    • ls -l: 等同于ll,查看文件详细信息
  • cd: 切换目录。

    • cd /: 切换到根目录
    • cd _ 或 cd ..: 切换上一级目录
    • cd ~: 切换到当前用户文件目录
    • cd -: 切换到之前的目录
  • pwd: 显示当前工作目录。

  • mkdir: 创建目录。

  • touch: 创建空文件或修改文件的时间戳。

  • cp: 复制文件或目录。

  • mv: 移动文件或目录。

  • rm: 删除文件或目录。

  • grep: 在文件中搜索指定文本。

  • find: 查找文件, find 路径 -name xx.txt

查看文件内容操作

  • cat : 命令用于连接多个文件并打印到标准输出
  • more: 命令逐页显示文件内容,可以按空格键翻页
  • less : 命令也是用于查看文件内容,但相比 more 具有更多功能。
  • head : 命令用于显示文件开头部分,默认显示前 10 行。
  • tail : 命令用于显示文件末尾部分,默认显示最后 10 行
  • grep : 命令用于在文件中搜索指定模式的内容,并输出匹配的行
  • awksed 是用于处理文本数据的强大工具,可以进行文本提取、替换等操作
  • 可以使用 less -f filename 命令来实时监控文件,在文件内容更新时自动刷新显示

系统信息查询

  • uname -a: 显示系统信息。
  • whoami: 显示当前用户名。
  • top: 实时显示系统中各个进程的资源占用情况。
  • free: 查看内存使用情况
  • netstat : 查看进程端口信息
  • ps: 显示系统中的进程(id,状态,占用资源)信息。
  • df: 显示磁盘空间使用情况。
  • du: 显示目录占用磁盘空间大小。

文件权限管理

  • chmod: 修改文件或目录的权限。
  • chown: 修改文件或目录的所有者。
  • chgrp: 修改文件或目录的所属组。

网络操作

  • ping: 测试网络连接。
  • ifconfig: 显示和配置网络接口信息。
  • netstat: 显示网络连接、路由表等信息。
  • ssh: 使用 SSH 协议登录远程主机。

软件包管理

  • apt: Ubuntu 系统下的软件包管理工具,如 apt install 安装软件包,apt update 更新软件包列表。
  • yum: CentOS 系统下的软件包管理工具,如 yum install 安装软件包,yum update 更新软件包列表。

压缩和解压

  • tar: 打包和解压文件。
  • zipunzip: 压缩和解压 Zip 文件。

用户操作

  • useradd: 添加用户
  • passwd: 设置密码
  • su: 切换用户
  • userdel: 注册用户

三剑客

  • grep: 过滤文本内容
    • grep -i: 忽略大小写
    • grep -n: 显示行号
    • grep ^abc: 显示以abc开头的内容
    • grep abc$: 显示abc结尾的内容
    • grep -B 2 -A 2: -B和-A。其中,-B参数用于显示匹配行之前的内容,-A参数用于显示匹配行之后的内容。
  • sed: 替换文件输出内容 - 默认不修改源文件
    • sed -i: 修改源文件
    • sed -i 's/文本1/文本2/g': 把文本内容中的文本1内容修改成文本2
    • sed -i 'n\文本1': 在第n行添加文本1内容
    • sed -i 'm, nd': 删除m到n行内容
    • sed -n 'm, np': 打印第m到n行内容
  • awk: 分析文件处理
    • awk '{print $1}': 打印第一列
    • awk -F: '{print $1}': 以分割符为: 打印第一列
    • awk '/关键字/': 搜索关键词的行
    • awk 'NR==1': 打印第一行内容

Git 常用操作

常用 Git 命令

  1. 基本操作
    • git init:初始化一个新的 Git 本地仓库。
    • git clone <repo_url>:克隆一个远程仓库。
    • git status:查看当前工作区和暂存区的状态。
    • git add:将文件添加到暂存区。
      • git add .: 添加所有改动文件
      • git add <file>: 添加指定文件
    • git commit -m "commit message":提交暂存区的更改。-m "commit message" 为附带信息
    • git commit --amend: 修改最后一次提交
    • git mv <old> <new>: 修改文件名
    • git rm <file>: 删除文件
    • git rm --cached <file>: 停止跟踪文件,但不删除文件
    • git push:将本地更改推送到远程仓库。
    • git pull:从远程仓库拉取最新的更改并合并到本地。
  2. 查看历史
    • git log:查看提交历史。
    • git log -p <file>: 查看指定文件提交历史
    • git log -n : 条数限制
    • git log --oneline: 单行显示
    • git log --graph: 图表日志
    • git blame <file>: 以列表的形式查看指定文件的提交历史
    • git diff:查看未暂存的变更内容。
    • git show <commit_hash>:查看某次提交的详细信息。
    • git reflog: 引用日志,几乎记录了HEAD节点和分支引用所指向的历史
  3. 日志筛选
    • 按日期
      • git log --after='2022-12-01' --before=2022-12-31
      • git log --before='today': 一般查看今天之前用yesterday
      • git log --before='30 day ago'
      • git log --before='4 week ago'
      • git log --before='1 month ago'
    • 按作者
      • git log --author='航'
      • git log --before='@qq.com'
    • 根据提交信息
      • git log --grep='重要信息'
  4. 分支操作
    • git branch:列出所有分支。
    • git branch <branch_name>:创建新分支。
    • git checkout <branch_name>:切换到指定分支。
    • git merge <branch_name> / git rebase:合并指定分支到当前分支。
      • merge
        • 优点
          • 更加安全:不会改变分支线
          • 保留分支历史:保留每个分支的所有历史
        • 缺点
          • 分支线混乱:每次合并会产生新的提交节点,导致分支线错综复杂
          • 分支间的关系不够清晰:无法准确看到那些合并产生的,那些来自原始分支
          • 一次合并容易把多个用户提交一起合并,不利于代码审查
      • rebase
        • 优点
          • 干净的提交历史
          • 保持分支间的关系清晰,不会创建新的合并提交
        • 缺点
          • 会改变提交记录和顺序
          • 不要在公共节点执行rebase
    • git format-patch -1 -o /Users/apple: 同步分支,获取其他分支的补丁
      • git format-patch: 补丁命令
      • -1: 最近一次提交
      • -o /Users/apple: 补丁文件路径
    • git stash save '暂存': 临时代码缓存
    • git stash list: 查看缓存队列
    • git stash apply stash@{id}: 进入相应缓存
  5. 提交撤销操作
    • git reset: git reset 会改变提交历史,建议不要在公共分支使用
      • git reset <file>: 移除提交到暂存区中的改动
      • git reset <commit-id>: 返回到某个提交,中间的内容会被丢弃
      • 重置的4中模式
        • --soft: 不改变工作目录和暂存区,将被回退的提交放入到暂存区
        • --mixed: 清空暂存区,将回退的变更和暂存区内容都放到工作目录中
        • --hard: 清空被回退的提交,暂存区和工作目录
        • --keep: 保留本地的变更,清除被回退的提交记录
    • git checkout HEAD <file>: 将指定文件恢复到当前分支内容
    • git revert <commit>: 撤销指定的提交内容,不影响其他内容

如何撤销更改?

  • 撤销未暂存的更改:

    1
    git checkout -- <file>  
  • 撤销已暂存的更改:

    1
    git reset HEAD <file>
  • 回滚到某个提交并丢弃后续更改:

    1
    git reset --hard <commit_hash>

如何回滚代码

  1. 使用 git reset 回滚至某个提交

    • 回到某个提交并保留未提交的更改:

      1
      git reset <commit_hash>
    • 回到某个提交并丢弃未提交的更改:

      1
      git reset --hard <commit_hash>  
  2. 使用 git revert 创建一个新提交

    • 如果希望撤销某次提交,并且保持项目历史完整,可以使用:

      1
      git revert <commit_hash>  
    • 这会生成一个新的提交,反转指定的提交。

  3. 软重置(Soft Reset):这会撤销最新的提交,并将更改保留在暂存区中。

    1
    git reset --soft HEAD^
  4. 硬重置(Hard Reset):这会撤销最新的提交,并将更改完全删除。

    1
    git reset --hard HEAD^

如何合并分支

  1. 确保当前在目标分支

    • 切换到想要合并到的分支,例如 main或master

      1
      git checkout main  
  2. 合并指定分支

    • 合并其他分支(例如 feature-branch)到当前分支:

      1
      git merge feature-branch  
    • 如果合并时没有冲突,Git 会自动完成合并。

    • 如果出现冲突,将会提示你手动解决冲突。

什么是“rebase”,它与“merge”有什么不同?

  • **merge**:保留所有分支的历史,创建一个新的合并提交。
  • **rebase**:将一个分支的更改“移动”到另一个分支的基础上,形成一条线性历史,不会留下合并提交。rebase 通常会使历史记录更加简洁。

解决合并冲突

  1. 编译冲突的文件。

  2. 手动编辑冲突的行。

  3. 保存文件后,使用:

    1
    git add <resolved_file>  
  4. 完成合并:

    1
    git commit -m "Merge branch 'feature-branch'"  

将工作区保存临时区域

要回到某个提交并保留未提交的更改,然后再回到原来的工作区,您可以按照以下步骤进行操作:

  1. 使用 git stash 命令将当前未提交的更改保存到一个临时区域(stash)中:

    1
    git stash  
  2. 使用 git log 命令查找您要回到的提交的哈希值(commit hash)。

  3. 使用 git checkout 命令切换到您要回到的提交:

    1
    git checkout <commit-hash>  
  4. 在回到的提交中进行必要的操作。

  5. 如果您完成了在回到的提交中的操作,并且想要回到原来的工作区,可以使用 git stash apply 命令将之前保存的更改应用到当前工作区:

    1
    git stash apply  

    如果您有多个 stash,可以使用 git stash list 命令查看 stash 列表,并使用 git stash apply stash@{<stash-index>} 应用特定的 stash。

adb操作

adb 常用命令

  1. adb devices - 列出当前连接的设备

  2. adb shell - 进入设备的shell命令行界面

  3. adb shell dumpsys window windows - 获取所有包名

    1
    2
    mFocusedApp=AppwindowToken{53309da token=Token{2e2fa785
    ActivityRecord{2928d4fc uo com.android.settings/.settingst 1127333}}}

    包名:com.android.settings (决定唯一性)
    界面名:.settings (一个界面对应一个界面名,类似于命名空间)

  4. adb shell am start -w 包名/界面名 - 获取app启动时间

    • ThisTime:界面启动耗时(ms)
    • TotalTime:应用自身启动耗时 = ThisTime + 应用application等资源启动时间(ms)
    • waitTime:系统启动应用耗时 = TotalTime + 系统资源启动时间(ms)
  5. adb install <apk文件路径> - 安装应用程序到设备

  6. adb uninstall <包名> - 卸载应用程序

  7. adb pull <设备文件路径> <保存到本地的路径> - 从设备中拷贝文件到本地

  8. adb push <本地文件路径> <设备文件路径> - 将本地文件推送到设备

  9. adb logcat - 查看设备的日志信息

    • 关于发生崩溃的时候,需要找到日志中”at” 前面,第一个字符是E的就是错误信息
  10. adb backup - 备份应用程序和数据

  11. adb restore - 恢复应用程序和数据

  12. adb reboot - 重启设备

性能

性能测试流程

  1. 明确测试需求( 需要压测的接口,预期结果等)
  2. 基于需求设计用例,方案,计划
  3. 准备测试数据,环境,脚本
  4. 运行脚本,进行日志、数据监听,最后进行结果分析
  5. 基于性能瓶颈做调优
  6. 回归测试,进行数据对比
  7. 整理发布测试报告

性能指标

  1. 用户角度

    响应时间

  2. 服务端角度

    rps 每秒请求数
    tps 每秒完成事务数

  3. 浏览器角度

    qps 每秒查询接口数
    hps 每秒点击率

image-20240723151501422

image-20240723151536922

性能瓶颈定位思路

  1. 首先排除压力自身问题,cpu,内存网络等限制
  2. 观察监控日志,定位问题处于那一阶段
  3. 排查网络是否达到上限
  4. 排查服务端机器负载情况 ( 内容与压力机相同)
  5. 观察服务端日志,是否存在错误日志,超时等
  6. 监控中间件连接数是否达到上限
  7. 监控程序线程状态
  8. 监控数据库,请求获取是否存在超时,或时间过长

见过那些性能问题

页面响应时间过长
页面分页卡死,sql查询数据量过大
导出excel时间过长,导致页面503
当用户达到一定高度时,服务器处理任务事件较长

性能测试和负载测试的区别。

性能测试是测试软件在不同负载下的性能表现,例如响应时间、吞吐量等;
负载测试是测试软件在各种负载条件下的表现和稳定性。
性能测试主要关注软件的性能指标,而负载测试主要关注软件在压力下的表现和行为。

负载

安全

高并发

栈,堆,队列的区别

栈、堆和队列是计算机科学中常用的数据结构,它们在内存管理和数据存储方面有着不同的特点和用途

栈(Stack):

  • 栈是一种后进先出(LIFO)的数据结构,也就是最后进入栈的元素最先被访问。
  • 栈通常在系统内存的较高地址分配空间,向低地址方向生长,存储局部变量、函数调用信息、程序中的临时数据等。
  • 栈的操作包括入栈(push)和出栈(pop),只有栈顶元素可见和可访问。
  • 栈的空间由系统自动管理,当栈内存不够时会发生栈溢出。

堆(Heap):

  • 堆是一种动态分配内存的数据结构,存储的数据没有特定的顺序,可以随机访问。
  • 堆通常在系统内存的较低地址分配空间,向高地址方向生长,存储动态分配的内存、对象、数据结构等。
  • 堆的内存需要手动分配和释放,如果没有正确释放堆内存可能会导致内存泄漏。
  • 堆是大部分动态数据结构和对象的存储地,如数组、链表、对象等都是在堆内存中分配的。

队列(Queue):

  • 队列是一种先进先出(FIFO)的数据结构,也就是先进入队列的元素先被处理。
  • 队列通常用于存储需要按顺序处理的数据,如任务调度、消息传递、缓冲等。
  • 队列有两个端点,分别是队头和队尾,数据从队尾入队,从队头出队。
  • 队列有不同类型,如普通队列、双端队列、优先队列等,用途和特点也有所不同。

举一个只能用使用队列,不能使用栈的业务场景

在一个打印机系统中,打印任务可以加入到一个打印队列中,打印机按照队列中任务的顺序依次打印

举一个只能用使用栈,不能使用队列的业务场景

实现后退功能,将浏览的记录项栈一样压入,后退时便从最外层开始

线性表和链表的区别

存储方式:

  • 线性表是一种顺序存储结构,数据元素存储在一段连续的内存空间中,可以通过元素的位置(索引)来访问。
  • 链表是一种链式存储结构,数据元素存储在内存中的节点中,并通过指针来连接不同节点,每个节点存储数据元素和指向下一个节点的指针。

插入和删除操作:

  • 在线性表中插入或删除一个元素可能需要移动其他元素的位置,需要移动的元素个数和位置取决于插入或删除的位置。
  • 在链表中插入或删除一个元素只需要调整节点的指针即可,不需要移动其他元素,因此插入和删除的时间复杂度是 O(1)。

动态扩展:

  • 线性表的存储空间是静态的,当空间不足时需要进行扩容操作,可能需要重新分配内存并复制数据。
  • 链表的存储空间是动态的,可以根据需要动态分配新的节点,不需要连续的内存空间。

访问效率:

  • 线性表通过索引可以直接访问任意位置的元素,访问时间复杂度为 O(1)。
  • 链表只能通过遍历链表来访问指定位置的元素,访问时间复杂度为 O(n)。

python锁类型

  • threading.Lock - 互斥锁 ,锁具有两个状态,分别是锁定和未锁定,可以用于多线程编程中对共享资源的互斥访问
  • threading.RLock - 可重入锁,可重入锁允许同一线程多次获得锁,而不会造成死锁。
  • threading.Semaphore - 信号量 ,可用来管理,当前能允许几个线程进行任务执行
  • threading.Event - 事件对象管理,当线程执行完后,会通知下一个线程执行,通过设置和清除标志来控制多个线程之间的通信。
  • ThreadPoolExecutor - 线程池,将任务提交给线程池,线程池会根据线程的状态,去执行线程任务
  • multiprocessing.Lock - 进程锁类型,用于在多个进程之间对共享资源进行互斥访问。
  • ProcessPoolExecutor - 进程池,可以创建一个包含多个进程的进程池,用于并发执行任务。不过它使用多个进程而不是线程来执行任务,适用于 CPU 密集型任务。

CPU密集型任务和IO密集型任务

  1. CPU密集型任务:
  • CPU密集型任务是指需要大量CPU计算资源的任务,例如大量的数学运算、图像处理、加密解密等。
  • 特点是需要长时间占用CPU进行计算,并且对CPU性能要求高。
  • 处理方式是通过增加并行计算的方式提高任务的执行效率,可以利用多线程、多进程、并发编程等技术来充分利用多核CPU的计算能力。
  1. IO密集型任务:
  • IO密集型任务是指任务主要耗费在IO操作上,例如文件读写、网络通信、数据库查询等。
  • 特点是任务在执行过程中会大量阻塞等待IO操作完成。
  • 处理方式是通过异步IO、非阻塞IO、事件驱动编程等技术来提高IO的效率,避免因为IO操作阻塞导致CPU资源空闲浪费。

在处理CPU密集型任务时,适合使用多线程或者多进程并行计算的方式来充分利用CPU资源;而在处理IO密集型任务时,需要注意IO操作的性能瓶颈,采取异步IO、事件驱动等方式来提高IO效率,避免因为阻塞IO操作而导致性能下降。

进程,线程,协程区别

  1. 进程(Process)
    • 进程是操作系统进行资源分配和调度的基本单位。每个进程都有自己独立的内存空间和系统资源,进程之间相互独立,通信需要通过进程间通信的方式来实现。
    • 创建和销毁进程的开销比较大,进程切换开销也比较大。因为每个进程都有自己独立的地址空间,需要进行上下文的切换。
    • 进程之间的通信需要通过共享内存、消息队列、管道等方式来实现。
  2. 线程(Thread)
    • 线程是进程的执行单元,多个线程可以共享同一个进程的资源。同一个进程内的线程可以共享相同的内存空间和资源,线程间的通信比进程间的通信更方便。
    • 创建和销毁线程的开销比较小,线程切换的开销也比较小。因为线程共享相同的地址空间,线程间的切换更为高效。
    • 但多线程编程需要考虑同步和互斥等问题,线程间的共享数据需要进行加锁操作。
  3. 协程(Coroutine)
    • 协程是一种轻量级的线程,协程的调度是由程序员控制的,可以手动挂起、恢复和切换执行。多个协程运行在同一个线程内,共享线程的资源。
    • 协程的切换开销更小,因为它是在用户态进行切换,不涉及内核态的上下文切换,因此效率更高。
    • 协程通信可以通过共享内存和消息传递等方式来实现。

并发问题 - 乐观锁和悲观锁

乐观锁(Optimistic Locking):

  • 乐观锁的实现方式是先尝试对共享资源进行操作,然后在更新资源时检查是否发生了冲突。
  • 当尝试更新资源时,乐观锁并不会立即对资源进行加锁,而是在更新之前先获取资源的版本号或标记。
  • 在更新完成后,再次比较资源的版本号或标记,如果发生冲突则进行回滚或重试操作。
  • 适用于读操作频繁,写操作较少发生冲突的场景。

悲观锁(Pessimistic Locking):

  • 悲观锁的实现方式是在访问共享资源之前先对资源进行加锁,以确保每次访问资源的独占性。
  • 当一个线程或进程对共享资源进行操作时,其他线程或进程会被阻塞,直到当前操作完成释放锁。
  • 悲观锁常见的实现方式包括互斥锁(Mutex)、读写锁(RWLock)等。
  • 适用于写操作频繁、存在并发冲突风险的场景。

软件测试原理

测试方法

image-20240723153426773

image-20240723153440885

image-20240723153459702

image-20240723153510510

image-20240723153521136

image-20240723153534502

测试用例组成要素

用例id,用例标题,模块,优先级,前置条件,测试步骤,测试数据,预期结果

测试报告包含哪些内容

测试项目介绍,测试时间,参与人,报告检查人( 一般是leader), 测试策略,测试内容,测试用例,bug数量,遗留bug/解决的bug,发现bug,测试通过率以及性能测试报告结果

软件测试的目标和原则是什么?

软件测试的目标包括验证软件是否实现了预期的功能,发现和消除软件中的缺陷,评估软件的可靠性和性能,确保软件符合规范和标准。软件测试的原则包括全面性、系统性、独立性、可重复性、有效性和透明性。

怎么分析测试点

  • 功能角度 输入 -> 处理 -> 输出
  • 用户角度 用接口验证业务

什么是测试用例?如何编写一个有效的测试用例?

测试用例是测试的基本单元,用于描述测试的输入、预期输出和执行步骤。编写有效的测试用例需要考虑覆盖所有可能情况,包括正常情况、边界情况和异常情况,以确保测试全面且有效。

什么是bug?怎样有效地报告和跟踪bug?

Bug是软件中的缺陷或错误,可以影响软件的功能或性能。有效的bug报告应包括问题描述、重现步骤、截图或录屏、错误类型等信息;跟踪bug可以使用缺陷跟踪系统或工具,记录bug的状态、处理进度和解决方案。

测试用例有什么用

  1. 便于梳理测试思路,确保覆盖测试点
  2. 便于工作量评估
  3. 提前准备测试数据
  4. 便于把控测试工作进度
  5. 便于回归测试
  6. 提高测试效率

做好测试用例设计的关键是什么

  1. 熟悉业务需求和用户使用场景
  2. 使用合适的测试用例设计方法
  3. 从不同维度了解功能,性能,安全,兼容等
  4. 了解开发相关技术栈实现方法

如何开展工作

确定测试目标和范围,制定测试计划,编写测试用例,搭建测试环境,执行测试用例,缺陷管理,报告和总结,完善测试流程

项目测试流程

需求分析和测试计划制定,用例设计/编写,冒烟测试阶段,集成测试阶段,系统测试阶段,上线前测试阶段,上线和后续维护阶段

如何提高用例覆盖率,减少漏测

  1. 根据需求文档编写用例,确保每条需求都被覆盖
  2. 充分理解业务,挖掘隐形需求
  3. 除了正常业务场景,也要考察一些异常的场景和数据
  4. 多维度进行测试,功能、性能、安全等方面考虑
  5. 模拟用户使用场景

如何保证用例的完整性

  1. 理解需求
  2. 指定完善的测试计划
  3. 使用不同的测试方法确保不同方面的预期功能
  4. 对边界条件和异常场景进行分析
  5. 编写详细的测试用例

B/S架构和C/S架构区别

  1. 部署方式
    • B/S架构:客户端通过浏览器访问Web应用程序,应用程序运行在服务器上,客户端无需安装额外的软件,只需浏览器即可。
    • C/S架构:客户端需要安装专用的客户端软件,与服务器端进行直接交互,客户端软件需要实现一部分业务逻辑。
  2. 数据处理
    • B/S架构:大部分数据处理和计算发生在服务器端,客户端主要用于显示和交互。
    • C/S架构:客户端和服务器端各自承担一部分数据处理和计算,客户端负责界面显示和一部分业务逻辑,服务器端负责数据存储和处理。
  3. 更新和维护
    • B/S架构:Web应用程序更新和维护只需在服务器端进行,客户端无需更新,可以减少客户端维护成本。
    • C/S架构:客户端软件需要定期更新和维护,包括修复bug、添加新功能等,客户端维护成本较高。
  4. 安全性
    • B/S架构:相对于C/S架构而言,B/S架构更容易实现安全性,因为客户端无法直接访问服务器上的资源。
    • C/S架构:客户端软件可能面临安全风险,例如可能被篡改、被恶意软件感染等,安全性管理需求更高。
  5. 跨平台兼容性
    • B/S架构:基于Web的B/S架构可以跨平台运行,用户只需有浏览器即可访问应用程序。
    • C/S架构:客户端软件需要根据不同的操作系统进行定制开发,可移植性较差。

app专项测试

弱网测试,兼容性测试,可靠性测试,更新测试

没有需求文档,如何开展测试

  1. 与相关开发,产品人员沟通,自己拟定一个文档,包括测试内容,功能需求,测试数据
  2. 参考其他相关产品,行业规范,进行总结梳理

一个项目如何开展测试

  1. 查找需求说明,项目设计等相关文档,分析需求
  2. 指定测试计划,确定测试范围和测试策略
  3. 设计测试用例,包括功能,兼容,性能,安全等方面
  4. 开展测试执行
  5. 回归测试以及发送测试报告

偶现bug怎么处理

尝试重现,截图保留整个流程数据,与开发产品沟通,如果是bug提交项目bug管理,持续跟踪bug问题。

如何确定不是一个bug

  1. 看需求文档判断
  2. 可以询问产品或者和开发沟通判定

如何区分前后端bug

  • 前端bug表现
    • 页面布局问题(如元素错位、样式不正确)
    • JavaScript错误(如功能失效、交互不灵敏)
    • 兼容性问题(如在不同浏览器或设备上表现不同)
    • 数据请求失败(如API调用未能返回预期数据) - 需要详细定位
  • 后端bug表现
    • API返回错误(如HTTP 500、404错误)
    • 数据库操作问题(如数据无法插入、查询不正确)
    • 业务逻辑错误(如功能不按照预期工作)
    • 性能问题(如响应时间过长)

一般会搭配postman,fiddler接口工具和数据库信息进行进一步验证

bug优先级和严重程度怎么划分

Bug 优先级(Priority)划分:

  • P0 - 紧急:严重影响软件的功能或安全性,导致软件无法正常使用,需要立即修复。
  • P1 - 高:影响核心功能或重要流程,需要快速修复,影响较大,优先级高。
  • P2 - 中:影响次要功能或正常流程,可以等待一段时间修复。
  • P3 - 低:影响较小,对用户体验影响有限,可以在后续版本中修复。

Bug 严重程度(Severity)划分:

  • 致命(Critical):导致系统崩溃或数据丢失,严重影响系统的正常功能。
  • 严重(Major):影响核心功能或关键流程,导致软件无法正常使用。
  • 一般(Normal):影响次要功能或流程,但软件仍可使用,用户体验略有影响。
  • 轻微(Minor):影响不大,对软件整体功能没有明显影响,用户体验基本不受影响。

划分依据:

  • 优先级划分:需要根据bug对软件功能和业务流程的重要性,以及影响范围和紧急程度。如果直接影响业务流程直接就是严重级别以上了。
  • 严重程度划分:考虑 bug 对系统的影响程度和用户体验程度。

线上出现bug怎么处理

  1. 首先评估严重程度,如果影响程序业务正常运行,且短期内不好修复,考虑代码回滚到稳定版本。测试环境下进行问题定位
  2. 如果能快速定位,并修复问题,测试后进行重新上线
  3. 如果是性能问题,尝试扩容,重启进行恢复。配合开发做问题定位和优化
  4. 如果只是一些优化性问题,先提交bug管理进行记录,下个版本一起解决
  5. 复盘,分析总结,避免后续出现类似问题

临近上线,发现bug怎么处理

  1. 首先和先关开发人员进行bug评估
  2. 轻微bug,可以考虑后续版本中修复
  3. 严重bug,看开发能否快速修复,和充足测试时间,如果不够看是否能延期上线,避免上线后造成严重的后果

什么是回归测试?为什么需要进行回归测试?

回归测试是修改或更新软件后重新执行现有测试用例,以确保修改没有引入新的缺陷;需要进行回归测试是因为软件的修改可能会影响其他部分的功能,需要验证软件整体的稳定性和一致性。

bug生命周期

  1. New:发现bug,提交给对应开发
  2. open:开发确认,进行修改
  3. fixed:开发人员进行修改后标识已修复,等待版本测试回归
  4. Rejected:如果不是bug,则开发会reject
  5. Dalay: 暂时不需要修改,后续版本中修改
  6. Closed:修复后测试通过后,关闭bug
  7. 后续出现bug时,新建bug,并链接之前bug

用例评审都有哪些人参加,怎么做,标准是什么

测试,开发,产品,qa,产品的领导

  1. 用例设计收费清晰合理,覆盖性,人手是否合理
  2. 优先级安排是否合理 ( 有时候产品会需要安排先测那些)
  3. 是否覆盖所有功能点
  4. 是否有很好的执行性

你认为在软件测试中,哪些方面是最容易出错的?如何避免这些问题?

在软件测试中,最容易出错的方面包括测试覆盖不全、测试计划和设计不完善、缺乏有效的测试数据、团队沟通不畅等。为了避免这些问题,可以加强团队协作和沟通、完善测试计划和设计、使用适当的工具和技术,提高测试的质量和效率。

测试工具开发

测试工具是如何开发的

  1. 需求分析:首先需要明确测试工具的功能需求和目标,包括要测试的应用类型、测试的覆盖范围、支持的测试类型等。根据需求分析确定测试工具的基本功能和设计方向。
  2. 设计架构:根据需求分析,设计测试工具的整体架构,包括模块划分、接口设计、数据流程等。确定测试工具的各个组件之间的交互关系和数据流动方式。
  3. 技术选型:根据设计需求和需求分析选择合适的技术栈和开发工具,确定开发语言、框架、数据库等,确保测试工具的性能和可扩展性。
  4. 开发实现:根据设计架构和技术选型开始开发测试工具,依次实现各个功能模块、组件、界面等,编写代码并进行测试验证,确保功能正确性和稳定性。
  5. 集成测试:各个功能模块开发完成后进行集成测试,确保各个模块之间的协同工作正常,并检查整体功能是否符合需求。
  6. 性能优化:针对性能瓶颈和性能优化点进行优化调整,提高测试工具的运行效率和性能,并确保测试结果的准确性和可靠性。
  7. 用户界面设计:设计用户友好的交互界面,使用户能够方便地使用和控制测试工具,提高用户体验。
  8. 编写文档:编写用户手册、技术文档等相关文档,包括功能介绍、安装配置、使用说明等,方便用户理解和使用测试工具。
  9. 测试验证:对测试工具进行全面的功能测试和回归测试,确保测试工具的功能正确性和稳定性。根据测试结果进行修复和优化。
  10. 发布和部署:完成测试工具的开发和测试后,进行发布和部署,向用户公开并提供支持和维护服务。监控用户使用情况反馈,根据用户需求和反馈进行后续版本迭代开发。

效能工具包含哪些,举几个例子

性能测试工具是用于评估系统、应用程序或设备性能的软件工具。这些工具可以帮助开发人员和测试人员了解系统在不同负载下的表现,并帮助识别性能瓶颈和提升性能。

  1. Apache JMeter
    • Apache JMeter 是一个功能强大的性能测试工具,可以模拟各种负载类型和场景,例如压力测试、负载测试、功能测试等,支持多种协议和服务。
  2. LoadRunner
    • LoadRunner 是 Micro Focus 公司推出的一款企业级性能测试工具,可以模拟用户在实际使用环境下的行为,对系统进行压力测试和性能测试。
  3. Gatling
    • Gatling 是一个基于 Scala 编程语言的现代化性能测试工具,具有高性能、可扩展性和易用性的特点,支持模拟大量并发用户。
  4. Locust
    • Locust 是一个开源的 Python 编写的性能测试工具,可以用于编写简单而可扩展的性能测试脚本,支持分布式测试和实时结果展示。

造数据工具,如何开发,提效多少

pass

python题

for循环while循环区别

  • for循环

    • for循环适合在已知循环次数的情况下使用。

    • 通常用于遍历一个序列,其中变量在每次循环时会取序列中的一个元素。

    • for循环会在入口前进行一次序列的索引赋值,然后按照给定的步长(默认为1)执行循环。

  • while循环
    • while循环适用于不知道具体循环次数,只知道终止条件。
    • while循环在每次循环开始之前检查一个条件,只有条件为真时才执行循环体。

列表(list)、元组(tuple)、字典(dictionary)和集合(set)区别

  1. 列表(list):
  • 列表是一种有序的、可变的数据结构,可以存储任意类型的数据。
  • 列表使用方括号[]来创建,元素之间用逗号,分隔。
  • 列表中的元素可以通过索引进行访问和修改。
  • 列表支持对元素的增加、删除和修改操作。

示例:

1
my_list = [1, 2, 3, "apple"]  
  1. 元组(tuple):
  • 元组是一种有序的、不可变的数据结构,也可以存储任意类型的数据。
  • 元组使用圆括号()来创建,元素之间用逗号,分隔。
  • 元组中的元素可以通过索引进行访问,但不能修改。
  • 元组适合用于一些不希望被修改的数据集合。

示例:

1
my_tuple = (1, 2, 3, "apple")  
  1. 字典(dictionary):
  • 字典是一种无序的、可变的数据结构,以键值对的形式存储数据。
  • 字典使用花括号{}来创建,键值对之间用冒号:分隔,键值对之间用逗号,分隔。
  • 字典中的数据通过键来访问,键必须是唯一的。
  • 字典适合存储具有关联性的数据。

示例:

1
my_dict = {"name": "Alice", "age": 25, "city": "New York"}  
  1. 集合(set):
  • 集合是一种无序的、可变的数据结构,用于存储独一无二的元素(不重复的元素)。
  • 集合使用花括号{}来创建,元素之间用逗号,分隔。
  • 集合中的元素不能重复,如果有重复元素会自动去重。
  • 集合支持一些集合间的运算,如并集、交集、差集等。

示例:

1
my_set = {1, 2, 3, 4, 4}  

了解多线程吗,了解python的GIL锁吗

  1. 多线程:多线程是一种并发编程的方式,允许程序同时执行多个线程,可以提高程序的响应速度和资源利用率。在 Python 中,由于 GIL 的存在,多线程并不能充分利用多核 CPU 的优势。在 CPU 密集型任务中,多线程可能无法提升性能,但在 I/O 密集型任务中或需要并发处理多个 I/O 操作时,多线程可以提高效率。
  2. 全局解释器锁(GIL):GIL 是 Python 解释器中的一个锁,用来保护对 Python 对象(包括内部对象和数据结构)的访问,使得在同一时刻只有一个线程可以执行 Python 代码。因为 GIL 的存在,Python 中的多线程实际上是假的并行,而是通过在单个 CPU 上切换多个线程来实现的并发。这意味着 Python 中的多线程无法利用多核 CPU 的优势。

由于 GIL 的存在,Python 的多线程并不适合 CPU 密集型的任务,但适合 I/O 密集型的任务,比如网络请求、文件操作等。对于 CPU 密集型的任务,可以考虑使用多进程来充分利用多核 CPU。

说一下python的线程和进程

  1. 进程

    • 进程是操作系统中的一个执行实体,每个进程拥有独立的内存空间,可以同时执行多个进程。

    • 在 Python 中,可以使用 multiprocessing 模块来创建和管理进程。每个进程有自己的全局解释器锁(GIL),因此在多核 CPU 上不受 GIL 的限制,可以实现真正的并行计算。

1
2
3
4
5
6
7
8
9
from multiprocessing import Process  

def foo():
print("Hello from a child process")

if __name__ == '__main__':
p = Process(target=foo)
p.start()
p.join()
  1. 线程

    • 线程是进程中的一个执行单元,多个线程共享相同的内存空间,可以实现并发执行。

    • 在 Python 中,可以使用 threading 模块来创建和管理线程。然而,由于全局解释器锁(GIL)的存在,Python 的多线程并不能实现真正的并行计算,适合 I/O 密集型任务。

1
2
3
4
5
6
7
8
import threading  

def foo():
print("Hello from a child thread")

t = threading.Thread(target=foo)
t.start()
t.join()

进程间通信方式有哪些

  1. 管道(Pipe)
    • 管道是一种双向通信机制,可以在两个进程之间传递数据。
    • 在 Python 中,可以使用 multiprocessing.Pipe 来创建管道并实现进程间通信。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from multiprocessing import Process, Pipe  

def sender(conn):
conn.send("Hello from sender")
conn.close()

def receiver(conn):
print(conn.recv())
conn.close()

parent_conn, child_conn = Pipe()
p1 = Process(target=sender, args=(child_conn,))
p2 = Process(target=receiver, args=(parent_conn,))
p1.start()
p2.start()
p1.join()
p2.join()
  1. 共享内存(Shared Memory)

    • 多个进程可以通过共享内存的方式来实现数据的共享和通信。

    • 在 Python 中,可以使用 multiprocessing.Valuemultiprocessing.Array 来创建共享内存对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
from multiprocessing import Process, Value  

def update_counter(counter):
counter.value += 1

counter = Value('i', 0)
p1 = Process(target=update_counter, args=(counter,))
p2 = Process(target=update_counter, args=(counter,))
p1.start()
p2.start()
p1.join()
p2.join()
print(counter.value)
  1. 队列(Queue)

    • 队列是一种先进先出(FIFO)的数据结构,可以在多个进程之间安全地传递数据。

    • 在 Python 中,可以使用 multiprocessing.Queue 来创建队列并实现进程间通信。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from multiprocessing import Process, Queue  

def producer(queue):
queue.put("Message from producer")

def consumer(queue):
message = queue.get()
print(message)

q = Queue()
p1 = Process(target=producer, args=(q,))
p2 = Process(target=consumer, args=(q,))
p1.start()
p2.start()
p1.join()
p2.join()

关于内存泄漏

内存泄漏是指程序中的一些对象一直占用着内存,但却无法被释放的情况。导致程序运行时消耗大量内存,最终导致程序崩溃或性能下降。

  1. 循环引用:当两个对象相互引用,但没有其他引用指向它们时,Python 的垃圾回收机制无法回收它们。
  2. 全局变量:如果一个对象被赋值给一个全局变量,即使在函数结束后也仍然存在于内存中。
  3. 未关闭文件:如果打开了文件但没有关闭,文件对象在程序运行期间会一直存在,导致内存泄漏。
  4. 缓存:缓存、缓冲区或其他数据结构中保存了大量对象,但没有及时清理或释放。

解决方法:

  1. 内存分析工具:使用工具如 memory_profiler、objgraph 等进行内存分析,查看对象的引用情况。
  2. 垃圾回收机制:了解 Python 的垃圾回收机制,包括循环引用和自动回收的机制。
  3. 资源管理:及时关闭文件、释放资源等,避免因资源未释放导致内存泄漏。
  4. 代码审查:定期审查代码,查找潜在的内存泄漏问题,及时解决。

编程题

手写diff函数 - 判断两个字符串差异

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
def diff_strings(a, b):  
"""
比较两个字符串 a 和 b,并返回它们之间的差异,类似于 unified diff 格式。

:param a: 第一个字符串
:param b: 第二个字符串
:return: 差异字符串
"""
# 将字符串按行分割
a_lines = a.splitlines()
b_lines = b.splitlines()

# 计算行数
len_a = len(a_lines)
len_b = len(b_lines)

# 初始化差异结果列表
diff = []

# 差异计算变量
i = 0
j = 0

while i < len_a and j < len_b:
if a_lines[i] == b_lines[j]:
i += 1
j += 1
else:
# 记录行号和差异段的开始
start_i, start_j = i + 1, j + 1
diff.append(f'@@ -{start_i},{len_a - i} +{start_j},{len_b - j} @@')

# 记录差异
while i < len_a and (j >= len_b or a_lines[i] != b_lines[j]):
diff.append(f'-{a_lines[i]}')
i += 1

while j < len_b and (i >= len_a or a_lines[i] != b_lines[j]):
diff.append(f'+{b_lines[j]}')
j += 1

# 添加剩余行
while i < len_a:
diff.append(f'-{a_lines[i]}')
i += 1

while j < len_b:
diff.append(f'+{b_lines[j]}')
j += 1

return '\
'.join(diff)

# 示例字符串
string_a = """Hello World
This is a test
Goodbye"""

string_b = """Hello World
This is a different test
Farewell"""

# 调用 diff_strings 函数
difference = diff_strings(string_a, string_b)
print(difference)

求最长公共子串

传入两个字符串,获取含有相同且最长的公共字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
'''
输入:
s1 = "abcdefg"
s2 = "xyabcde"
输出:"abcde"
'''


def diffStr(s1: str, s2: str) -> str:
# 获取两个字符串的长度
m = len(s1)
n = len(s2)

# 构建二维数组dp,用于存储最长公共子串的长度
dp = [[0] * (n + 1) for _ in range(m + 1)]

# 初始化最长公共子串的长度和结束索引
max_length = 0
end_index = 0

# 遍历两个字符串,使用动态规划的方法填充dp数组
for i in range(1, m + 1):
for j in range(1, n + 1):
if s1[i - 1] == s2[j - 1]:
# 如果当前位置字符相同,则在前一个位置的基础上加1
dp[i][j] = dp[i - 1][j - 1] + 1
# 更新最长公共子串长度和结束索引
if dp[i][j] > max_length:
max_length = dp[i][j]
end_index = i
else:
# 如果当前位置字符不同,则长度清零
dp[i][j] = 0

# 返回最长公共子串
return s1[end_index - max_length:end_index]


s1 = "abcdefgrefcanfsasd"
s2 = "xywerdefcanfsasd"

str1 = diffStr(s1, s2)
print(str1)

词频统计

线程交替打印奇偶数

最长回文字串