在软件测试领域,测试数据的生成是一个至关重要的环节。手动生成数据不仅耗时耗力,而且难以保证数据的质量和多样性。因此,使用在线测试数据生成器成为了许多测试人员的首选。

本文将介绍6种超牛的在线测试数据生成器,帮助你更高效地生成测试数据。

1、Mockaroo

Mockaroo
是一款在线数据生成工具,提供超过125种数据类型,包括姓名、地址、日期、图片等。用户可以自定义数据结构,生成符合实际需求的测试数据。

在线地址:
https://www.mockaroo.com/

特点:

  • 支持大量数据类型和自定义结构
  • 一次可生成高达100万条数据
  • 支持多种数据导出格式(如CSV、JSON、SQL等)

2、JSON-Generator

JSON-Generator
主要专注于生成 JSON 格式的测试数据。

在线地址:
https://json-generator.com/

特点:

  • 针对 JSON 优化:它对 JSON 数据结构有着深入的理解和优化。用户可以轻松地定义 JSON 对象、数组等复杂结构。例如,可以快速生成一个包含多个嵌套对象和数组的 JSON 数据,用于测试处理复杂 JSON 数据的 API 或系统。
  • 可视化编辑界面:拥有一个直观的可视化编辑界面,即使是没有深厚技术背景的用户也能快速上手。通过简单的拖放和设置参数操作,就可以构建出复杂的 JSON 数据结构。例如,用户可以在界面上添加新的 JSON 属性、设置属性的值类型和范围,然后立即看到生成的 JSON 数据示例。
  • 代码生成功能:除了直接生成 JSON 数据外,JSON - Generator 还可以生成用于生成 JSON 数据的代码。这对于需要在程序中动态生成 JSON 数据的开发人员来说非常方便。用户可以选择不同的编程语言(如 JavaScript、Python 等),生成相应的代码片段。

3、SQL-Data Generator

SQL-Data Generator
是专门为生成用于 SQL 数据库测试数据而设计的工具。

在线地址:
https://sqldatagenerator.com/generator

特点:

  • 数据库兼容性强:它支持多种主流的 SQL 数据库,如 MySQL、Oracle、SQL Server 等。无论你的测试环境使用哪种数据库管理系统,都可以使用 SQL - Data - Generator 生成合适的数据。例如,它可以根据不同数据库的特定数据类型和语法规则,生成符合要求的插入语句或数据文件。

  • 数据库架构感知:能够识别数据库的架构信息,根据表结构、字段类型、约束条件等生成有效的测试数据。如果数据库中有表之间的关联关系(如外键约束),SQL - Data - Generator 可以生成符合这些关系的数据。例如,在生成订单数据和客户数据时,会根据订单表中的客户外键关系,确保生成的订单数据中的客户 ID 与客户表中的数据相匹配。

  • 数据更新和删除模拟:除了生成插入数据外,还可以模拟数据库中的数据更新和删除操作。用户可以指定数据更新的规则和条件,以及删除数据的范围,用于测试数据库的更新和删除功能的正确性和性能。例如,可以设置按照一定比例更新某些字段的值,或者根据特定条件删除部分数据,以模拟实际数据库操作中的各种情况。

4、Generatedata

Generatedata
是一款开源的在线测试数据生成器,它支持30多种数据类型,包括姓名、电子邮件、地址、电话号码等,并支持多种导出格式,如CSV、SQL、JSON等。此外它还支持自定义数据类型和生成规则,用户可以创建复杂的数据结构,生成大量测试数据。且支持REST API,可以方便地进行数据生成和集成。

在线地址:
https://generatedata.com/generator

特点:

  • 开源、免费
  • 支持自定义数据类型和生成规则
  • 支持多种数据导出格式(如CSV、JSON、SQL等)

5、Randat

Randat
是一个免费的在线工具,可以生成包含随机个人信息的表格,如姓名、年龄、职业、薪水等。用户只需选择首选列和行数,然后点击“Generate”按钮即可生成表格。生成的表格可以以XLS、XLSX或CSV格式导出,方便用户进行后续处理。Randat非常适合用于生成具有特定属性的测试数据。

在线地址:
http://www.randat.com/

6、DatabaseTestData

DatabaseTestData
用于生成测试数据。它允许用户基于现有数据模型进行自定义,以重现表结构或从头开始创建一个表。这使得数据生成更加贴近实际场景,提高了测试数据的可用性。DatabaseTestData界面简洁易用,适合快速生成测试数据。

在线地址:
https://www.databasetestdata.com/

小结

测试数据生成器是软件测试中不可或缺的工具,它们可以自动生成大量具有各种属性的测试数据,从而节省时间和精力,提高测试效率。本文介绍的8种在线测试数据生成器各具特色,适用于不同的测试场景和需求。在选择测试数据生成器时,应根据自己的实际需求进行选择和配置,以生成高质量的测试数据,确保软件质量和稳定性。

本文是对公开论文的核心提炼,旨在进行学术交流。如有任何侵权问题,请及时联系号主以便删除。

来源:晓飞的算法工程笔记 公众号,转载请注明出处

论文: Open-RAG: Enhanced Retrieval-Augmented Reasoning with Open-Source Large Language Models

创新性


\({\tt Open-RAG}\)
具备以下特点:

  1. 将任意稠密的
    LLM
    转变为参数高效的稀疏专家混合(
    sparse mixture of experts

    MoE
    )模型,能够处理复杂的推理任务,包括单步和多步查询。
  2. 特别地训练模型应对看似相关但实际上具有误导性的挑战性干扰,同时仅在适配器中扩展
    MoE
    ,保持模型的规模。
  3. 通过结合结构学习、架构转换和基于反思的生成,利用潜在嵌入学习动态选择相关专家并有效整合外部知识,以实现更准确和上下文支持的响应生成及其有效性的估计。
  4. 通过一种混合自适应检索方法,以确定检索的必要性,并在性能提升与推理速度之间取得平衡。

内容概述


检索增强生成(
Retrieval-Augmented Generation

RAG
)能够提高大型语言模型(
Large Language Models

LLMs
)的准确性,但现有方法往往在有效利用检索证据方面表现出有限的推理能力,尤其是在使用开源
LLMs
时。

论文提出
\({\tt Open-RAG}\)
,旨在增强开源
LLMs

RAG
的推理能力。为了控制开源
LLMs
的行为,生成更具上下文支持的响应,采用来自
Self-RAG
的基于反思的生成方法,用四种特殊的反思标记类型增强输出词汇(对应上图中的蓝色部分):检索(
Retrieval
)、相关性(
Relevance
)、基础(
Grounding
)和实用(
Utility
)。


\({\tt Open-RAG}\)
LLM 定义为一个模型
\(\mathcal{M}_{G}\)
,该模型在给定输入查询
\(q\)
的情况下,目标生成一个包含
\(m\)
个标记的输出序列
\(o = [o_1, o_2, ..., o_m]\)
,其处理过程如下:

  • 在训练过程中,模型学习生成指示是否需要检索以回答
    \(q\)
    的检索标记([
    RT
    ]/[
    NoRT
    ])。在推理过程中,则采用混合自适应检索方案,综合检索标记和模型置信度判断是否需要检索。

  • 如果不需要检索,
    \(\mathcal{M}_{G}\)
    仅使用
    LLM
    的参数知识生成响应(即将
    \(o\)
    作为
    \(y_{pred}\)
    返回)。

  • 如果需要检索,对于来自外部知识源
    \(D = \{d_i\}_{i=1}^{N_d}\)
    的单步或多步,使用用户定义的冻结检索器
    \(R\)
    来检索前
    \(k\)
    个文档
    \(S = \{s_t\}_{t=1}^{k}\)
    ,其中每个
    \(s_t\)

    \(\{r_j\}_{j=1}^{N_H}\)
    组成,
    \(r_j \in D\)

    \(N_H\)
    表示步数。


    • 对于每个检索到的内容
      \(s_t\)

      \(\mathcal{M}_{G}\)
      生成一个相关性标记、输出响应
      \(y_t\)
      、一个基础标记和一个实用标记。


      • 相关性标记([
        Relevant
        /
        Irrelevant
        ])指示
        \(s_t\)
        是否与
        \(q\)
        相关。

      • 基础标记([
        Fully Supported
        /
        Partially Supported
        /
        No Support
        ])指示
        \(y_t\)
        是否得到
        \(s_t\)
        的支持。

      • 实用标记([U:
        1
        ]-[U:
        5
        ])定义
        \(y_t\)

        \(q\)
        的有用程度。

    • 并行处理每个
      \(s_t\)
      并通过对它们(即所有
      \(y_t\)
      )进行排名来生成最终答案
      \(y_{pred}\)
      ,排名依据是对应预测的相关性、基础和实用标记的归一化置信度的加权和。

Open-RAG


数据收集

为了使
\({\tt Open-RAG}\)
能够处理无检索查询,以及需要检索的单步和多步查询,使用各种类型的任务和数据集构建训练数据。给定原始数据集中的输入输出数据对 (
\(q\)
,
\(y\)
),通过利用真实标签注释或
LLM
\(C\)
生成标记来增强数据,从而创建监督数据。

如果
\(C\)
添加的相应检索标记是 [
RT
],则会进一步增强数据,根据以下方式创建三种不同的新标记。

  1. 使用
    \(R\)
    检索前
    \(k\)
    个文档
    \(S\)
    。对于每个检索到的文档
    \(s_t\)

    \(C\)
    评估
    \(s_t\)
    是否相关,并返回相关性标记。为了解决单步和多步查询的问题,为数据管道配备了一个步数统一启发式:如果至少有一个段落
    \(\{r_j\} \in s_t\)
    是相关的,则将相关性标记添加为 [
    Relevant
    ],否则使用 [
    Irrelevant
    ]。
  2. 当预测为 [
    Relevant
    ] 时,为了使
    \(\mathcal{M}_{G}\)
    能够在
    \(s_t\)
    中更细致地区分有用和干扰上下文,设计了一个数据对比启发式:(i)对于单步
    RAG
    数据集,直接使用
    \(C\)
    来标记基础标记;(
    ii
    )对于多步
    RAG
    数据集,如果所有段落
    \(\{r_j\} \in s_t\)
    被单独预测为 [
    RT
    ],那么将 [
    Fully Supported
    ] 添加为基础标记;否则,使用 [
    Partially Supported
    ]。
  3. 无论相关性标记的预测如何,都使用
    \(C\)

    \(y\)
    提供相对于
    \(q\)
    的实用分数。

参数高效的
MoE
微调

RAG
任务本质上是复杂的,由单一(单步)或多个(多步)段落的查询等各种组件组成。根据这些复杂性选择性地利用模型的不同部分,可以促进对多样化输入上下文的更自适应和更细致的推理能力。

因此,论文采用稀疏升级(
sparse upcycling
)将
\(\mathcal{M}_{G}\)
转换为
MoE
架构,并根据需要动态学习为每个具有多样化复杂性的查询(例如,单步/多步)选择性激活最合适的专家。这种选择性激活是通过前面量身定制的训练数据进行学习(微调)的,确保模型能够区分有用信息和误导信息。

稀疏
MoE
\({\tt Open-RAG}\)
模型通过一个参数高效的
MoE
转换块增强了密集主干
LLM

FFN
层,该转换块由一组专家层
\(\mathbf{E} = \{\mathcal{E}_e\}_{e=1}^{N_E}\)
以及有效的路由机制组成。

每个专家层包含一个复制的原始共享
FFN
层权重,通过具有参数
\(\theta_e\)
的适配器模块
\(\mathcal{A}_{e}\)
进行了适配。为了确保参数高效性,在每个专家中保持
FFN
层不变,仅训练适配器模块
\(\mathcal{A}_{e}\)
。通过这种方式,只需存储一个
FFN
副本,保持模型大小不变,除了适配器和路由模块中参数的增加。其余层,例如
Norm

Attention
,则从密集模型中复制。

\[\begin{equation}
\mathcal{A}_{e}(x) = \sigma(x W_{e}^{down}){W_{e}^{up}} + x.
\end{equation}
\]

对于给定输入
\(x\)
,路由模块
\(\mathcal{R}\)
根据注意力层的归一化输出
\(x_{in}\)

\(N_E\)
个专家中激活
\(\texttt{Top-}k\)
个专家。考虑
\(W_{|\cdot|}\)
表示相应专家模块的权重,将路由模块定义如下:

\[\begin{equation}
\mathcal{R}(x_{in}) = \text{Softmax}(\texttt{Top-}k(W_{\mathcal{R}} \cdot x_{in}))
\end{equation}
\]

\({\tt Open-RAG}\)
模型的高效性源于以下设置:
\(|\theta_e| = |W_{e}^{down}| + |W_{e}^{up}| \ll |\phi_o|\)
,其中在微调过程中保持密集
LLM

\(\phi_o\)
不变。

最后,将参数高效的专家模块的输出
\(y\)
表达为:

\[\begin{equation}
y = \sum_{e=1}^{N_E} \mathcal{R}(x)_e \mathcal{A}_e (\mathcal{E}_e(x)).
\end{equation}
\]

混合自适应检索

由于
LLM
具有不同的参数知识,论文提出了一种混合自适应检索方法,该方法基于模型信心提供两种阈值选择,按需检索并平衡性能和速度。

在训练过程中,
\(\mathcal{M}_{G}\)
学习生成检索反映标记([
RT
] 和 [
NoRT
])。在推理时,通过将 [
NoRT
] 添加到输入中,测量基于强制不检索设置的输出序列
\(o\)
的信心,从而得出
\(\hat{q} = q \oplus \texttt{[NoRT]}\)
。设计了两种不同的信心分数
\(f_{|\cdot|}\)
: (
i
)
\(f_{minp}\)
,即单个标记概率的最小值,以及 (
ii
)
\(f_{meanp}\)
,即生成序列中单个标记概率的几何平均值。

\[\begin{align}
\label{eq:meanpscore}
f_{minp}(o | \hat{q}) &= \min_{i=1}^{m} p(o_i|\hat{q}, o_{<i}) \\
f_{meanp}(o| \hat{q}) &= \sqrt[m]{\prod_{i=1}^{m} p(o_i|\hat{q}, o_{<i})}
\end{align}
\]

通过可调阈值
\(\gamma\)
控制检索频率,当
\(f_{|\cdot|}<\gamma\)
时进行检索。

主要实验




如果本文对你有帮助,麻烦点个赞或在看呗~
更多内容请关注 微信公众号【晓飞的算法工程笔记】

work-life balance.

【引言】

简体繁体转换器是一个实用的小工具,它可以帮助用户轻松地在简体中文和繁体中文之间进行转换。对于需要频繁处理两岸三地文档的用户来说,这样的工具无疑是提高工作效率的好帮手。本案例将展示如何利用鸿蒙NEXT提供的组件和服务,结合第三方库@nutpi/chinese_transverter,来实现这一功能。

【环境准备】

• 操作系统:Windows 10
• 开发工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806
• 目标设备:华为Mate60 Pro
• 开发语言:ArkTS
• 框架:ArkUI
• API版本:API 12
• 三方库:chinese_transverter

【实现步骤】

1. 项目初始化

首先,确保你的开发环境已经安装了鸿蒙NEXT的相关工具链。然后,创建一个新的鸿蒙NEXT项目。

2. 引入第三方库

使用ohpm命令安装@nutpi/chinese_transverter库:

ohpm install @nutpi/chinese_transverter

3. 编写核心逻辑

接下来,在项目的主组件中引入所需的模块,并定义好状态变量和方法。这里的关键在于设置监听器以响应输入文本的变化,并调用转换函数来获取转换结果。

import { transverter, TransverterType, TransverterLanguage } from "@nutpi/chinese_transverter";

@Entry
@Component
struct SimplifiedTraditionalConverter {
  @State @Watch('onInputTextChanged') inputText: string = '';
  @State simplifiedResult: string = '';
  @State traditionalResult: string = '';
  @State isInputFocused: boolean = false;

  onInputTextChanged() {
    this.simplifiedResult = transverter({
      type: TransverterType.SIMPLIFIED,
      str: this.inputText,
      language: TransverterLanguage.ZH_CN
    });
    this.traditionalResult = transverter({
      type: TransverterType.TRADITIONAL,
      str: this.inputText,
      language: TransverterLanguage.ZH_CN
    });
  }

  private copyToClipboard(text: string): void {
    const clipboardData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, text);
    const systemClipboard = pasteboard.getSystemPasteboard();
    systemClipboard.setData(clipboardData);
    promptAction.showToast({ message: '已复制到剪贴板' });
  }

  build() {
    // UI构建代码...
  }
}

4. 构建用户界面

在build方法中,我们构建了应用的用户界面。这里主要包括一个可滚动的容器、输入区域、结果展示区以及操作按钮。

Scroll() {
  Column() {
    Text("简体繁体转换器")
      .width('100%')
      .height(54)
      .fontSize(18)
      .fontWeight(600)
      .backgroundColor(Color.White)
      .textAlign(TextAlign.Center)
      .fontColor(this.textColor);

    // 输入区域与清空按钮
    Column() {
      // ...省略部分代码...

      Text('清空')
        .borderWidth(1)
        .borderColor(this.themeColor)
        .fontColor(this.themeColor)
        .height(50)
        .textAlign(TextAlign.Center)
        .borderRadius(10)
        .fontSize(18)
        .width(`${650 - this.basePadding * 2}lpx`)
        .margin({ top: `${this.basePadding}lpx` })
        .onClick(() => this.inputText = "");
    }
    .width('650lpx')
    .padding(`${this.basePadding}lpx`)
    .margin({ top: 20 })
    .backgroundColor(Color.White)
    .borderRadius(10)
    .alignItems(HorizontalAlign.Start);

    // 结果展示区
    // ...省略部分代码...
  }
  .width('100%')
  .height('100%')
  .backgroundColor("#f2f3f5")
  .align(Alignment.Top)
  .padding({ bottom: `${this.basePadding}lpx` });
}

【完整代码】

// 导入必要的转换库,提供简体与繁体之间的转换功能
import { transverter, TransverterType, TransverterLanguage } from "@nutpi/chinese_transverter";
// 导入剪贴板服务,用于将文本复制到系统剪贴板
import { pasteboard } from '@kit.BasicServicesKit';
// 导入提示服务,用于向用户显示消息
import { promptAction } from '@kit.ArkUI';

// 使用@Entry装饰器标记此组件为应用的入口点
@Entry
  // 使用@Component装饰器定义一个名为SimplifiedTraditionalConverter的组件
@Component
struct SimplifiedTraditionalConverter {
  // 定义状态变量inputText,存储用户输入的原始文本,当其值变化时触发onInputTextChanged方法
  @State @Watch('onInputTextChanged') inputText: string = '';
  // 定义状态变量simplifiedResult,存储转换后的简体结果
  @State simplifiedResult: string = '';
  // 定义状态变量traditionalResult,存储转换后的繁体结果
  @State traditionalResult: string = '';
  // 定义状态变量isInputFocused,表示输入框是否获得了焦点
  @State isInputFocused: boolean = false;
  // 定义主题颜色
  @State private themeColor: string = '#439fff';
  // 定义文本颜色
  @State private textColor: string = "#2e2e2e";
  // 定义基础内边距大小
  @State private basePadding: number = 30;
  // 定义最小文本区域高度
  @State private minTextAreaHeight: number = 50;
  // 定义最大文本区域高度
  @State private maxTextAreaHeight: number = 300;

  // 当inputText状态改变时触发的方法,用于更新转换结果
  onInputTextChanged() {
    // 将inputText转换为简体,并更新simplifiedResult
    this.simplifiedResult = transverter({
      type: TransverterType.SIMPLIFIED,
      str: this.inputText,
      language: TransverterLanguage.ZH_CN
    });
    // 将inputText转换为繁体,并更新traditionalResult
    this.traditionalResult = transverter({
      type: TransverterType.TRADITIONAL,
      str: this.inputText,
      language: TransverterLanguage.ZH_CN
    });
  }

  // 将给定的文本复制到剪贴板,并显示提示信息
  private copyToClipboard(text: string): void {
    const clipboardData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, text); // 创建剪贴板数据
    const systemClipboard = pasteboard.getSystemPasteboard(); // 获取系统剪贴板
    systemClipboard.setData(clipboardData); // 设置剪贴板数据
    promptAction.showToast({ message: '已复制到剪贴板' }); // 显示复制成功的提示
  }

  // 构建组件的UI
  build() {
    Scroll() { // 创建可滚动的容器
      Column() { // 在滚动容器中创建垂直布局
        // 创建标题文本
        Text("简体繁体转换器")
          .width('100%')
          .height(54)
          .fontSize(18)
          .fontWeight(600)
          .backgroundColor(Color.White)
          .textAlign(TextAlign.Center)
          .fontColor(this.textColor);

        // 创建用户输入区域
        Column() {
          // 创建多行文本输入框
          TextArea({ text: $$this.inputText, placeholder: '请输入简体/繁体字(支持混合输入)' })
            .fontSize(18)
            .placeholderColor(this.isInputFocused ? this.themeColor : Color.Gray)
            .fontColor(this.isInputFocused ? this.themeColor : this.textColor)
            .borderColor(this.isInputFocused ? this.themeColor : Color.Gray)
            .caretColor(this.themeColor)
            .onBlur(() => this.isInputFocused = false) // 当输入框失去焦点时,更新isInputFocused状态
            .onFocus(() => this.isInputFocused = true) // 当输入框获得焦点时,更新isInputFocused状态
            .borderWidth(1)
            .borderRadius(10)
            .backgroundColor(Color.White)
            .constraintSize({ minHeight: this.minTextAreaHeight, maxHeight: this.maxTextAreaHeight });

          // 创建清空按钮
          Text('清空')
            .borderWidth(1)
            .borderColor(this.themeColor)
            .fontColor(this.themeColor)
            .height(50)
            .textAlign(TextAlign.Center)
            .borderRadius(10)
            .fontSize(18)
            .width(`${650 - this.basePadding * 2}lpx`)
            .margin({ top: `${this.basePadding}lpx` })
            .clickEffect({ level: ClickEffectLevel.HEAVY, scale: 0.8 }) // 添加点击效果
            .onClick(() => this.inputText = ""); // 清空输入框
        }
        .width('650lpx')
        .padding(`${this.basePadding}lpx`)
        .margin({ top: 20 })
        .backgroundColor(Color.White)
        .borderRadius(10)
        .alignItems(HorizontalAlign.Start);

        // 创建繁体结果展示与复制区域
        Column() {
          // 创建繁体结果标题
          Text(`繁体结果:`)
            .fontWeight(600)
            .fontSize(18)
            .fontColor(this.textColor);

          // 创建繁体结果展示文本
          Text(`${this.traditionalResult}`)
            .constraintSize({ minHeight: this.minTextAreaHeight, maxHeight: this.maxTextAreaHeight })
            .fontColor(this.themeColor)
            .fontSize(18)
            .textAlign(TextAlign.Start)
            .copyOption(CopyOptions.InApp)
            .margin({ top: `${this.basePadding / 3}lpx` });

          // 创建复制繁体结果按钮
          Text('复制')
            .enabled(this.traditionalResult ? true : false) // 只有当有繁体结果时,按钮才可用
            .fontColor(Color.White)
            .backgroundColor(this.themeColor)
            .height(50)
            .textAlign(TextAlign.Center)
            .borderRadius(10)
            .fontSize(18)
            .width(`${650 - this.basePadding * 2}lpx`)
            .margin({ top: `${this.basePadding}lpx` })
            .clickEffect({ level: ClickEffectLevel.HEAVY, scale: 0.8 })
            .onClick(() => this.copyToClipboard(this.traditionalResult)); // 复制繁体结果到剪贴板
        }
        .width('650lpx')
        .padding(`${this.basePadding}lpx`)
        .backgroundColor(Color.White)
        .borderRadius(10)
        .margin({ top: `${this.basePadding}lpx` })
        .alignItems(HorizontalAlign.Start);

        // 创建简体结果展示与复制区域
        Column() {
          // 创建简体结果标题
          Text(`简体结果:`)
            .fontWeight(600)
            .fontSize(18)
            .fontColor(this.textColor);

          // 创建简体结果展示文本
          Text(`${this.simplifiedResult}`)
            .constraintSize({ minHeight: this.minTextAreaHeight, maxHeight: this.maxTextAreaHeight })
            .fontColor(this.themeColor)
            .fontSize(18)
            .textAlign(TextAlign.Start)
            .copyOption(CopyOptions.InApp)
            .margin({ top: `${this.basePadding / 3}lpx` });

          // 创建复制简体结果按钮
          Text('复制')
            .enabled(this.simplifiedResult ? true : false) // 只有当有简体结果时,按钮才可用
            .fontColor(Color.White)
            .backgroundColor(this.themeColor)
            .height(50)
            .textAlign(TextAlign.Center)
            .borderRadius(10)
            .fontSize(18)
            .width(`${650 - this.basePadding * 2}lpx`)
            .margin({ top: `${this.basePadding}lpx` })
            .clickEffect({ level: ClickEffectLevel.HEAVY, scale: 0.8 })
            .onClick(() => this.copyToClipboard(this.simplifiedResult)); // 复制简体结果到剪贴板
        }
        .width('650lpx')
        .padding(`${this.basePadding}lpx`)
        .backgroundColor(Color.White)
        .borderRadius(10)
        .margin({ top: `${this.basePadding}lpx` })
        .alignItems(HorizontalAlign.Start);
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor("#f2f3f5")
    .align(Alignment.Top)
    .padding({ bottom: `${this.basePadding}lpx` });
  }
}

1.简介

前面几篇宏哥介绍了两种(java和maven)环境搭建和浏览器的启动方法,这篇文章宏哥将要介绍第一个自动化测试脚本。前边环境都搭建成功了,浏览器也驱动成功了,那么我们不着急学习其他内容,首先宏哥搭建好的环境中创建首个完整的自动化测试脚本,让小伙伴或者童鞋们提前感受感受,也是为了激起大家的学习兴趣。

宏哥的个人经验是:自动化脚本编写比较容易,最大的困难去如何去写测试断言。自动化测试,最重要的还是落在测试上面,而不是自动化,自动化只是手段。断言的写法,就考验出一个测试工程师是否考虑全面,体现出你的用例编写水平。

2.测试用例

很多童鞋或者小伙伴们可能会有这样有个误区:自动化测试是不需要设计测试用例的。其实不然它也是需要设计测试用例,然后根据用例进行脚本的编写和断言,只不过是把用例以代码的形式体现出来,而机器恰好可以识别代码,将代码跑起来,其实就是在执行你的用例,只不过是由机器帮你自动执行。好了废话少说开始说说宏哥今天要做的测试是:打开百度,输入北京-宏哥搜索,验证打开链接有没有北京-宏哥博客园的链接。

测试用例:打开百度首页,搜索:北京-宏哥,然后检查搜索列表,有没有 北京-宏哥 博客园链接。后续文章为了避免不必要的麻烦和错误,宏哥都在maven搭建的环境中进行实战演示。

2.1步骤

1.启动浏览器

2.打开百度首页:http://www.baidu.com

3.判断这个页面是不是我们提前知道的页面

4.定位搜索输入框,记录下输入框元素的id定位表达式:#kw

5.定位搜索提交按钮(百度一下),获取id定位表达式:#su

6.在搜索输入框输入:北京-宏哥,点百度一下这个按钮

7.在搜索结果列表去判断是否存在北京-宏哥博客园这个链接

8.退出浏览器,结束测试

2.2代码设计

2.3参考代码

packagecom.bjhg.playwright;importcom.microsoft.playwright.Browser;importcom.microsoft.playwright.BrowserType;importcom.microsoft.playwright.Locator;importcom.microsoft.playwright.Page;importcom.microsoft.playwright.Playwright;/***@author北京-宏哥
*
* @公众号:北京宏哥(微信搜索,关注宏哥,提前解锁更多测试干货)
*
* 《刚刚问世》系列初窥篇-Java+Playwright自动化测试- 5-创建首个自动化脚本(详细教程)
*
* 2024年7月12日
*/ public classLaunchChrome {

@SuppressWarnings(
"deprecation")public static voidmain(String[] args) {try (Playwright playwright =Playwright.create()) {//使用chromium浏览器,# 浏览器配置,设置以GUI模式启动Chrome浏览器(要查看浏览器UI,在启动浏览器时传递 headless=false 标志。您还可以使用 slowMo 来减慢执行速度。 Browser browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setHeadless(false).setSlowMo(50));//创建page Page page =browser.newPage();//浏览器打开百度 page.navigate("https://www.baidu.com/");//判断title是不是 百度一下,你就知道 try{
String baidu_title
= "百度一下,你就知道";assert baidu_title ==page.title();
System.out.println(
"Test Pass");

}
catch(Exception e){
e.printStackTrace();
}
//定位搜索输入框,输入北京-宏哥 page.locator("#kw").type("北京-宏哥");//定位搜索提交按钮(百度一下)。点击 page.locator("#su").click();//这里通过元素XPath表达式来确定该元素显示在结果列表,从而判断Selenium官网这个链接显示在结果列表。 Locator ele_string=page.locator("//*[@id='1']/div/div[1]/h3/a");

String ele_string1
=ele_string.innerText();

System.out.println(ele_string1);
try{if(ele_string1.equals("北京-宏哥 - 博客园")){

System.out.println(
"Testing is successful!");

}
}
catch(Exception e){

e.printStackTrace();

}
//关闭page page.close();//关闭browser browser.close();
}
}

}

2.4运行代码

1.运行代码,右键Run AS->java Application,就可以看到控制台输出,如下图所示:

2.运行代码后电脑端的浏览器的动作。如下图所示:

3.小结

3.1equals和==的区别

宏哥这里简单的说一下,更详细的你可以百度查一下。关于判断两个字符串是否相等的问题。在编程中,通常比较两个字符串是否相同的表达式是“==”,但在java中不能这么写。在java中,用的是equals();

例:A字符串和B和字符串比较:
if(A.equals(B)){

}
返回true 或false.

String 的equals 方法用于比较两个字符串是否相等。由于字符串是对象类型,所以不能用简单的“==”判断。而使用equals比较两个对象的内容是否相等。

注意:equals()比较的是对象的内容(区分字母的大小写格式),但是如果使用“==”比较两个对象时,比较的是两个对象的内存地址,所以不相等。即使它们内容相等,但是不同对象的内存地址也是不相同的。

好了,今天时间也不早了,首个Java+Playwright的自动化测试脚本就分享到这里,感谢你耐心地阅读!

【环境】kos5.8sp2, kernel5.10

最近工作中需要搭建一个软件环境,其依赖的 glibc 版本较高,因此在安装时给出了以下错误:

xxx: /lib64/libc.so.6: version 'GLIBC_2.33' not found (required by xxx)

去查看当前机器的 libc.so 支持的 GLIBC 版本,发现确实太低了:

strings /usr/lib64/libc.so.6 | grep GLIBC

而且,难受的是,这个需要安装的软件仅仅提供了一个二进制安装程序,没办法基于其源码做定制化改动。

这样看来,不得不升级当前系统的 glibc 了。

网上有很多有关替换 glibc 的教程,大都是给出了
make && make install
的方案。
然而这种方案是及其风险的。
因为 glibc 是系统的核心库之一,几乎所有的用户空间程序都依赖于它。如果不考虑风险直接
make install
,那么当新的 glibc 安装成功后,你的系统大概率会挂掉。一个有代表性的现象是,你执行一些诸如
ls
的简单 shell 指令都会报错了。

其实无痛安装 glibc 有更好的办法,那就是基于 glibc 的 rpm 源码包在本地环境编译成 rpm,再进行安装。

我的当前系统为 kos5.8SP2,与 RHEL 同根同源。因此,我找了一个 Fedora 的 glibc 安装包:
glibc-2.38-19.fc39.src.rpm
,下面开始编译。

mkdir glibc-2.38 && cd glibc-2.38

# 拆分 src.rpm
rpm2cpio ../glibc-2.38-19.fc39.src.rpm | cpio -div

# 手动拷贝到 rpmbuild/SOURCE
cp -r * ~/rpmbuild/SOURCE/

# 进入源码目录
cd ~/rpmbuild/SOURCE/

# 安装依赖
yum builddep glibc.spec

# 开始编译
rpmbuild -ba glibc.spec --nodebuginfo

编译成功后,会在
~/rpmbuild/RPMS/
目录下生成 rpm,安装即可:

cd ~/rpmbuild/RPMS/ && yum install *

安装成功后,验证一下,glibc 已经更新了: