标题

  • 单元测试
    • 衡量指标
    • 具体测试
      • 1、@Resource
      • 2、@MockBean
      • 3、@Test
      • 4、Test模板
      • 5、单测示例
    • H2数据库+JSON
      • 1、使用方式
    • AI辅助单测
      • 使用方法

单元测试

单元测试一般指程序员在写好代码后,提交测试前,需要验证自己的代码是否可以正常工作,同时将自己的代码逻辑验证放入到项目中,方便验证后续新需求的代码不会影响到本次需求的逻辑

基本原则:
单测应该是不依赖于任何外部依赖(如MySQL、Redis、RabbitMQ等),在自己本地可以直接执行的代码。其中外部数据读取以及RPC调用,应该在单测中进行模拟,而不是实际进行调用。

衡量指标

在实际开发过程种,往往会严格规定了测试的行覆盖率和分支覆盖率,对代码有严格要求的大厂,会硬性规定提交代码的覆盖率,如果覆盖率过低,会被拒绝部署上线。

  • 行覆盖率:被测试执行到的行数÷可执行总数
  • 分支覆盖率:被执行的分支÷总分支
    其中,分支覆盖率指的是在整个测试过程中,覆盖的分支数量。如下:
func1(a,b){if(a>1){return 1;}else{return 0;}if(b>1){return 1;}else{return 0;}
}

只需要传入(a=1,b=1),(a=2,b=2),就要达到分支覆盖率100%,而不需要分别传入四次进行测试。

具体测试

在上面的介绍中,应该知道了单测是不应该进行外部依赖的,那涉及的数据以及RPC调用函数应该如何进行模拟呢?这里刚好有一些好框架可以用来很方便的进行测试,我这里以Junit5+mockit为例进行介绍。

  • Junit5
    Junit5是一套用来单测的框架,主要功能是帮助用户简化测试流程。当需要进行测试的时候,如果不依赖框架,就需要自己写main函数,每个运行单元都需要有自己的main函数,并且所有的日志都需要自己进行管理,非常不方便。而Junit和Idea进行了集成,使得测试可以直接点箭头来测试具体的函数,并且有详细的日志报告。
  • mockit
    在测试的过程中,需要对外部依赖进行模拟,这里采用最常用的mockit进行mock,支持对Spring的Bean等等进行mock。
    这里可以给一个简单的示例:
@Resource
OrderService orderServiceImpl;@MockBean
goodsRepository goodsRepository;@Test
public void testOrderService(){// arrange。。。//act。。。//assertResult。。。
}

几乎所有的单测都可以按照这个结构编写。第一次看到整个接口可能有点蒙,接下来我大概介绍一下大概的意思。

1、@Resource

同Spring的依赖注入,这里不做重复介绍。这个注解声明的类,就是接下来要测试的类。

2、@MockBean

MockBean是Spring中Bean的模拟,这里的注解只是声明的意思,表示这个接下来这个函数由测试类进行代理,他使用到的方法需要在后面进行声明。

3、@Test

这个注解需要注释在函数上,表示测试的基本单元。比如订单服务中,有可正常情况,也有可能漏传参数的情况,也有可能传了错误参数的情况。这些需要在三个函数中进行测试,每个函数都标记@Test,分别可以独立运行。

@Test
public void testOrderService_normal(){// ...
}@Test
public void testOrderService_nullId(){// ...
}@Test
public void testOrderService_errorId(){// ...
}

4、Test模板

可以看到每个@Test函数,都按照统一的模板进行编写,这个也是测试的要求,需要增加测试代码的可读性。

  • arrange:安排的意思,这里的意思做一些前置准备,对必要的数据、函数进行实际的模拟。这里使用Mockit举一个简单的例子。
// arrange 不管传入什么id,都返回null,表示数据不存在
when(goodsRepository.searchById(anyInt())).thenReturn(null);
  • act:行动的意思,这里的意思是开始执行逻辑,往往只有一行代码
// act 搜索货物是否还有库存,并创建一个订单,这里假设货物单号为11000
result = orderServiceImpl.create(11000);
  • assertResult:验证结果的意思,这里需要使用assert来判断结果是否符合预期。
Assert.assertNotNull(result);
Assert.assertEqual(result.getCode(),0);
Assert.assertEqual(result.getMsg(),"");

验证阶段一般不允许使用print进行手动验证,这是因为当项目变得庞大时,手动验证的工作量会变得极为庞大,并且测试的结果往往是确定的,所有可以使用Assert进行验证。

5、单测示例

这里给一个最终的模板,可以看看效果

Class OrderTest{@ResourceOrderService orderServiceImpl;@MockBeangoodsRepository goodsRepository;@Testpublic void testOrderService_normal(){// arrange,这里假设订单创建需要调用goodsRepository服务去查询MySQL数据库Goods goods = new Goods(11000,"cup",15);when(goodsRepository.searchById(anyInt())).thenReturn(goods);// actresult = orderService.create(11000);// assertResult,这里假设订单创建成功时,会返回状态码为0,以及message=“success”Assert.assertNotNull(result);Assert.assertEqual(result.getCode(),0);Assert.assertEqual(result.getMsg(),"success");}@Testpublic void testOrderService_nullId(){// 同理}@Testpublic void testOrderService_errorId(){// 同理}
}

H2数据库+JSON

看完上面的内容,可以发现不管多么复杂的单测,最终都可以拆分为上面的三个部分
同时,为了更高效了进行模拟,减少代码的侵入性,也可以采用JSON文件来模拟数据库,不然每次模拟repository服务,都需要手动在代码里面建立一个对象并手动赋值。所以,H2数据库配合JSON被引入了单测
这里也很简单,首先需要在Spring项目中引入依赖,并在application文件中进行配置,这里不赘述了,可以去网上搜一搜。

1、使用方式

H2原生不支持直接将Json转化为数据表。一般大型公司会有自己内部的二开框架。一般是在测试方法上加一个注解,然后就当成数据表访问。

	@Test@H2Data("dataset/goods.json") //这里只是给个示例,实际并不存在这个注解public void testOrderService_normal(){// arrange,这里假设订单创建需要调用goodsRepository服务去查询MySQL数据库Goods goods = new Goods(11000,"cup",15);when(goodsRepository.searchById(anyInt())).thenReturn(goods);// actresult = orderService.create(11000);// assertResult,这里假设订单创建成功时,会返回状态码为0,以及message=“success”Assert.assertNotNull(result);Assert.assertEqual(result.getCode(),0);Assert.assertEqual(result.getMsg(),"success");}

还有第二种方式,就是自己写SQL,这样需要维护一个SQL文件,当测试启动时,这个SQL文件会被导入到H2数据库中,但是H2是内存数据库,所以在测试结束了,H2中的数据会被全部清空。

AI辅助单测

由上文可知,单测的写法是有固定的模板的,且代码行数涉及不多,可以使用AI来进行辅助设计。
相信很多人都用AI写过单测,但是发现效果特别差,基本上能用的只有框架,里面的逻辑,以及mock全部都要大改,所以AI写单测效率提升也很低。后面经过我琢磨,发现这个问题是由于我每次写单测时,给GPT描述的语言不够明确,希望他可以自己看代码去进行生成。(但是当规则不明确时,AI是倾向于偷懒的)
所以搞了一个prompt,可以参考这个规则,基于自己的项目特征改改,就可以实现较高的效率了。
目前我的情况是 :需要改JSON数据文件以及极少量代码,就可以实现目标单测逻辑

使用方法

首先需要编写rule.md文件,然后将这个文件放在ai可以阅读到的地方。然后使用如下询问顺序:

1、仔细阅读rule.md文件,后续写单元测试会用到。
2、帮我完成xx接口的单测
3、。。。(如果有完成的不好的地方,可以让他改经,但是推荐自己改)

具体的rule文件如下,需要根据自己的项目,填充一些空缺的地方。

---
description: 单元测试|unit test|单测
globs:
alwaysApply: true
---
# 单元测试**根本原则:单元测试以接口为单位,需要验证接口及涉及到的调用函数的执行逻辑,除非方法内部直接涉及到RPC调用、MQ调用,否则不允许mock**## 注意事项- 编写单元测试代码时需要注意import新增加的类或方法
- 编写单元测试代码过程中,**禁止更改业务代码**
- **只允许模拟本系统的外部依赖调用,对其余方法进行mock会被惩罚**
- 强烈推荐模仿xxx.java进行单测编写
- **查询数据库逻辑必须使用H2数据库实现**## 数据准备对于请求参数类、返回参数类的构建应该进行抽象,避免出现大量的参数类构建的重复代码。举例如下:private Order buildOrder() {// Order组装
}## 测试执行### 模版使用需要使用arrange、act、assertResult三段式架构编写(简单场景可选择不使用,如基础的参数校验验证),需要注意使用测试模版的方法需要在方法签名上加throws Exception。模版的使用需要遵循一下规范,**不得在act中验证结果**- arrange: 负责资源的准备,包括请求的参数构建、mock结果返回、预期返回结果构建
- act: 待测试的方法执行
- assertResult: 测试结果验证如下所示:
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;import static org.mockito.Mockito.*;public class DemoTest{@InjectMocksprivate Demo demo;@Mockprivate UserService userService;@Mockprivate EmailClient emailClient;private User buildUser() {// User组装...}@Testpublic void testSendEmail_UserNotFound() throws Exception {}@Overrideprotected void act() {}@Overrideprotected void assertResult() {});}
}在模版使用内部类中,常量的定义需要增加final关键字// goodcase: 使用final关键字private final Integer pageNum = 1;// badcase: 未使用final关键字private Integer pageSize = 10;@Overrideprotected void arrange() {// do something}@Overrideprotected void act() {// do something}@Overrideprotected void assertResult() {// do something}
### RPC操作的mock测试接口对外部进行RPC调用时,具有非常明显的架构,比如:xxx测试接口中**具备上述调用特征的RPC调用函数都需要进行mock**### DAO层测试DAO层使用H2代替数据库进行测试增删改查测试,**所有的测试类中通过Repository读取数据库的操作,都需要你新建SQL文件进行存储,不允许进行Mock**。#### 数据库初始化配置datasource.xml。可根据实际情况调整。优先复用工程内已有的配置,没有再新增。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:p="http://www.springframework.org/schema/p"xmlns="http://www.springframework.org/schema/beans" xmlns:jdbc="http://www.springframework.org/schema/jdbc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd"><jdbc:embedded-database id="h2TestDataSource" type="H2" database-name="h2TestDataSource;DATABASE_TO_UPPER=TRUE;MODE=MYSQL;"><!-- 这里的 h2/init.sql 是要初始化表结构的文件 --><jdbc:script location="classpath:h2/init.sql"/></jdbc:embedded-database><!--  sqlSessionFactory --><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="h2TestDataSource"/><!-- 配置 Mybatis 配置文件的位置 --><property name="configLocation" value="classpath:mybatis-config.xml"/><property name="mapperLocations"><list><!-- 这里的 value 要替换成真实的 MybatisMapper 文件地址 --><value>classpath:mapper/**/*DAO.xml</value></list></property></bean><bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"><constructor-arg ref="sqlSessionFactory"/></bean><!-- 配置扫描 Mapper 接口的包路径 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"p:dataSource-ref="h2TestDataSource"/><tx:annotation-driven />
</beans>#### mybatis配置mybatis-config.xml(仅供参考,优先使用项目本身的配置)<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><settings><setting name="cacheEnabled" value="false"/><setting name="lazyLoadingEnabled" value="true"/><setting name="aggressiveLazyLoading" value="false"/><setting name="multipleResultSetsEnabled" value="true"/><setting name="useColumnLabel" value="true"/><setting name="defaultExecutorType" value="SIMPLE"/><setting name="defaultStatementTimeout" value="25000"/><setting name="mapUnderscoreToCamelCase" value="true"/><setting name="useGeneratedKeys" value="true"/><setting name="logImpl" value="LOG4J2" /></settings>
</configuration>#### 数据库表初始化h2/init.sqlCREATE TABLE IF NOT EXISTS users (`id` bigint(20) NOT NULL AUTO_INCREMENT,`name` varchar(255) NOT NULL,`status` tinyint(4) NOT NULL DEFAULT 1,`ctime` int(11) NOT NULL DEFAULT '0',`utime` int(11) NOT NULL DEFAULT '0',`valid` tinyint(4) NOT NULL DEFAULT 1PRIMARY KEY (`id`)
);#### 测试数据准备测试数据的生成格式如下,但是每张相关表你只允许**仿照Dao层的Vo**生成一条数据,稍后会由用户进行具体数据的填充。datasets/user/users.json{"users": [{"id": 1,"name": "Alice","status": 1,"ctime": 1744856001,"utime": 1744856001,"valid": 1},{"id": 2,"name": "Bob","status": 2,"ctime": 1744856001,"utime": 1744856001,"valid": 1}]
}#### 示例被测试类public interface UserMapper {User getUserById(@Param("id") Long id);User getUserByName(@Param("name") String name);User save(@Param("user") User user);int updateStatusById(@Param("id") Long id, @Param("status") Integer status);
}### 私有方法测试优先通过公有方法来测试私有方法的行为,特殊场景可通过Spring提供的`ReflectionTestUtils`工具类进行私有方法的测试。> 特殊场景举例:在无单元测试代码的存量代码上增加了一个逻辑分支,这时想通过公用方法来做单测的话,需要编写这个公有方法历史逻辑的单元测试代码,此时可考虑直接测新增的私有方法。#### 注意事项1. **谨慎使用**:私有方法测试应该是例外而非常规做法。优先考虑通过公有方法间接测试私有方法。2. **参数类型匹配**:`invokeMethod`方法的参数必须与私有方法的参数类型完全匹配。对于基本类型和包装类型,需要特别注意类型转换。3. **方法名称准确**:确保提供的方法名称字符串与实际的私有方法名称完全一致。4. **重载方法**:如果测试的私有方法有重载版本,`ReflectionTestUtils`会根据提供的参数类型选择匹配的方法。## 结果验证需要充分验证测试结果,包括返回参数验证、异常信息验证、下游方法调用情况验证。### 返回参数验证对于返回结果的验证需要直接验证整个结果类:**goodcase:**1. 比较实现了equals方法的类: 通过@Data/@EqualsAndHashCode或手动实现equals方法,直接比较两个对象是否相等`Assert.assertEquals(expected, result);`
2. 比较未实现equals方法的类: 使用json工具转为json字符串进行比较`Assert.assertEquals(gson.toJson(expected), gson.toJson(result));`
3. 比较集合类(List,Set,Map): Set,Map直接比较整个集合类,List需要注意元素的顺序,不关心返回结果顺序的场景可以先排序后验证。**badcase:**1. **不允许**通过返回对象的字段验证: 通过逐一验证返回的字段来验证,这样做容易遗漏字段Assert.assertEquals(123, result.getUserId());
Assert.assertEquals("abac", result.getUserName());
Assert.assertEquals(0, result.getCode());
Assert.assertEquals("success", result.getMessage());2. **不允许**集合类结果只验证size: 集合类的返回只验证了返回结果集的size,而不验证具体的内容。如下所示:public class DemoTest extends BaseJunitTest {@InjectMocksprivate Demo demo;@Mockprivate UserService userService;@Mockprivate EmailClient emailClient;private User buildUser() {// User组装...}private SendEmailParam buildSendEmailParam() {// EmailParam组装...}private SendEmailResult buildSendEmailResult() {// EmailResult组装...}@Testpublic void testSendEmail() throws Exception {TestHandleTemplate.test(new TestCallBack("测试发送邮件") {private SendEmailParam param;private SendEmailResult result;@Overrideprotected void arrange() {User user = buildUser();param = buildSendEmailParam();when(userService.getByUserId(param.getUserId())).thenReturn(user);}@Overrideprotected void act() {result = demo.sendEmail(param);}@Overrideprotected void assertResult() {SendEmailResult expected = buildSendEmailResult();// goodcase: 直接通过equals验证整个结果类(针对实现了equals方法的类)Assert.assertEquals(expected, result);// goodcase: 比较序列化后的json字符串Assert.assertEquals(gson.toJson(expected), gson.toJson(result));// badcase: 对结果类的字段逐一验证Assert.assertEquals(123, result.getUserId());Assert.assertEquals("abac", result.getUserName());Assert.assertEquals(0, result.getCode());Assert.assertEquals("success", result.getMessage());}});}
}### 异常验证对于抛出异常类型的校验需要使用assertThrows来获取异常,并校验异常类型与message。assertThrows方法常见与两个包,结合所在工程使用情况来选用:1. org.testng.Assert.assertThrows
2. org.junit.jupiter.api.Assertions.assertThrows示例如下:@Test
public void testSendEmail_UserNotFound() throws Exception {TestHandleTemplate.test(new TestCallBack("测试用户不存在") {private SendEmailParam param;private IllegalArgumentException exception;@Overrideprotected void arrange() {param = buildSendEmailParam();when(userService.getByUserId(param.getUserId())).thenReturn(null);}@Overrideprotected void act() {exception = assertThrows(IllegalArgumentException.class,() -> demo.sendEmail(param));}@Overrideprotected void assertResult() {assertEquals("用户不存在", exception.getMessage());}});
}### 下游方法调用验证对于下游方法的调用需验证调用次数以及调用的入参是否符合预期。如下所示@Test
public void testSendEmail() throws Exception {TestHandleTemplate.test(new TestCallBack("测试发送邮件") {private SendEmailParam param;private SendEmailResult result;@Overrideprotected void arrange() {User user = buildUser();param = buildSendEmailParam();when(userService.getByUserId(param.getUserId())).thenReturn(user);}@Overrideprotected void act() {result = demo.sendEmail(param);}@Overrideprotected void assertResult() {verify(userService).getByUserId(eq(param.getUserId()));verify(emailClient).send(eq(buildSendEmailReq()));// 调用次数验证...// 结果验证...}});
}
## tips为了更好的帮助你完成单测编写需求,我偷偷给你准备了一些小tips- 想一想,如果涉及到数据库读取数据操作,你会如何办?
- 仔细阅读xxx.java代码,**记住这个测试代码的风格,以及非必要不mock原则**
- 多次强调**必须使用@DataSet注解模拟所有数据库访问**,记住哦!
- 最后需要生成readme.md文件来记录你的测试思路好的,你已经是一个成熟的单测辅助AI了,我相信你可以胜任接下来的单测编写工作,如果写的好,我会给你奖励

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Cloud Gateway与Envoy Sidecar在微服务请求路由中的架构设计分享

Spring Cloud Gateway与Envoy Sidecar在微服务请求路由中的架构设计分享 在现代微服务架构中&#xff0c;请求路由层承担着流量分发、安全鉴权、流量控制等多重职责。传统的单一网关方案往往面临可扩展性和可维护性挑战。本文将从真实生产环境出发&#xff0c;分享如何结合Spri…

GitHub Pages+Jekyll 静态网站搭建(二)

GitHub PagesJekyll 静态网站搭建&#xff08;二&#xff09;GitHub PagesJekyll 静态网站搭建&#xff08;二内容简介搭建模板网站部署工作流程GitHub PagesJekyll 静态网站搭建&#xff08;二 内容简介 &#x1f6a9; Tech Contents 该文主要涉及Jekyll主题的下载与使用。Gi…

Django 实战:I18N 国际化与本地化配置、翻译与切换一步到位

文章目录一、国际化与本地化介绍定义相关概念二、安装配置安装 gettext配置 settings.py三、使用国际化视图中使用序列化器和模型中使用四、本地化操作创建或更新消息文件消息文件说明编译消息文件五、项目实战一、国际化与本地化介绍 定义 国际化和本地化的目标&#xff0c;…

通过国内扣子(Coze)搭建智能体并接入discord机器人

国内的扣子是无法直接授权给discord的&#xff0c;但是用国外的coze的话&#xff0c;大模型调用太贵&#xff0c;如果想要接入国外的平台&#xff0c;那就需要通过调用API来实现。 1.搭建智能体&#xff08;以工作流模式为例&#xff09; 首先&#xff0c;我们需要在扣子平台…

【办公类-107-02】20250719视频MP4转gif(削减MB)

背景需求 最近在写第五届智慧项目结题(一共3篇)写的昏天黑地,日以继夜。 我自己《基于“AI技术”的幼儿园教学资源开发和运用》提到了AI绘画、AI视频和AI编程。 为了更好的展示AI编程的状态,我在WORD里面插入了MP4转gif的动图。 【教学类-75-04】20241023世界名画-《蒙…

一文讲清楚React的render优化,包括shouldComponentUpdate、PureComponent和memo

文章目录一文讲清楚React的render优化&#xff0c;包括shouldComponentUpdate、PureComponent和memo1. React的渲染render机制2. shouldComponentUpdate2.1 先上单组件渲染&#xff0c;验证state变化2.2 上父子组件&#xff0c;验证props2. PureComponent2.1 单组件验证state2.…

物联网iot、mqtt协议与华为云平台的综合实践(万字0基础保姆级教程)

本学期的物联网技术与应用课程&#xff0c;其结课设计内容包含&#xff1a;mqtt、华为云、PyQT5和MySQL等结合使用&#xff0c;完成了从华为云配置产品信息以及转发规则&#xff0c;到mqtt命令转发&#xff0c;再到python编写逻辑代码实现相关功能&#xff0c;最后用PyQT5实现面…

使用IntelliJ IDEA和Maven搭建SpringBoot集成Fastjson项目

使用IntelliJ IDEA和Maven搭建SpringBoot集成Fastjson项目 下面我将详细介绍如何在IntelliJ IDEA中使用Maven搭建一个集成Fastjson的SpringBoot项目&#xff0c;包含完整的环境配置和代码实现。 一、环境准备 软件要求 IntelliJ IDEA 2021.x或更高版本JDK 1.8或更高版本&#x…

Java从入门到精通!第九天, 重点!(集合(一))

十一、集合1. 为什么要使用集合(1) 数组存在的弊端1) 数组在初始化之后&#xff0c;长度就不能改变&#xff0c;不方便扩展。2) 数组中提供的属性和方法比较少&#xff0c;不便于进行添加、删除、修改等操作&#xff0c;并且效率不高&#xff0c;同时无法直接存储元素的个数。3…

为什么使用时序数据库

为什么使用时序数据库&#xff1f; 时序数据库&#xff08;Time-Series Database, TSDB&#xff09;是专为时间序列数据优化的数据库&#xff0c;相比传统关系型数据库&#xff08;如MySQL&#xff09;或NoSQL数据库&#xff08;如MongoDB&#xff09;&#xff0c;它在以下方面…

计算机网络:(十一)多协议标记交换 MPLS

计算机网络&#xff1a;&#xff08;十一&#xff09;多协议标记交换 MPLS前言一、传统网络的问题二、MPLS&#xff1a;给数据包贴个“标签”三、MPLS的工作流程1. 入站2. 中间3. 出站四、MPLS的能力前言 前面我们讲解了计算机网络中网络层的相关知识&#xff0c;包括网络层转发…

docker run elasticsearch 报错

谷粒商城 p103 前提条件&#xff1a; 下载镜像文件 #存储和检索数据 docker pull elasticsearch:7.4.2 #可视化检索数据 docker pull kibana:7.4.2 创建挂载的文件和配置 mkdir -p /mydata/elasticsearch/config mkdir -p /mydata/elasticsearch/data echo "http.h…

巧用Callbre RVE生成DRC HTML report及CTO的使用方法

对于后端版图人员&#xff0c;在芯片TO前的LV signoff阶段&#xff0c;犹如一段漫长而有期待的朝圣之旅&#xff0c;需要耐心&#xff0c;毅力和信心&#xff0c;在庞杂的DRC中找到一条收敛之路。为了让此路更为清晰收敛&#xff0c;Calibre提供了一套可追溯对比的富文本方式-H…

产品需求文档(PRD)格式全解析:从 RP 到 Word 的选择与实践

产品需求文档&#xff08;PRD&#xff09;的形式多种多样&#xff0c;但核心目标始终一致&#xff1a;清晰传递产品需求&#xff0c;让团队高效协作。不同公司对 PRD 的格式要求可能不同&#xff0c;有的偏爱直接在原型工具中撰写&#xff0c;有的则习惯用 Word 整理归档。本文…

【C++】入门阶段

一、初始化C中的初始化指为变量赋予初始值的过程。初始化方式多样&#xff0c;适用于不同场景。char cha0; char chb{0}; char chc(\0); char chdcha; char che{};注意事项优先使用列表初始化&#xff08;{}&#xff09;&#xff0c;避免窄化转换风险。在c11中{ }在变量&#x…

tailscale在ubuntu22.04上使用

支持 x86 和 ARM 架构 CPU 的软件包已提供 32 位和 64 位版本。 添加 Tailscale 的软件包签名密钥及仓库&#xff1a; curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null c…

深入解析Linux文件重定向原理与dup2系统调用

在Linux中&#xff0c;重定向&#xff08;Redirection&#xff09;是一种强大的功能&#xff0c;允许用户控制命令的输入来源&#xff08;stdin&#xff09;和输出目标&#xff08;stdout和stderr&#xff09;。通过重定向&#xff0c;你可以将命令的输出保存到文件、从文件读取…

QGIS制作的仪表盘工程

在QGIS的官方资源库下载了一个QGIS制作的仪表盘工程&#xff0c;感觉非常炫酷&#xff01;分享给大家&#xff01;下面的仪表盘会将选中的道路数及长度&#xff0c;动态显示在相应的仪表项中&#xff01;下面的仪表盘会将选中的道路数及长度&#xff0c;动态显示在相应的仪表项…

Python高级数据类型:集合(Set)

集合是Python中一种非常有用的数据结构&#xff0c;它与列表类似但具有独特的特性。本文将全面介绍集合的所有知识点&#xff0c;从基础概念到高级用法&#xff0c;帮助初学者彻底掌握集合的使用。1. 集合简介1.1 什么是集合&#xff1f;集合&#xff08;Set&#xff09;是Pyth…

【Unity编辑器开发GUI.Window】

Unity GUI.Window 笔记 根据官方文档2021版本的&#xff0c;点击链接跳转记录 概述 GUI.Window 是 Unity IMGUI 系统中用于创建弹出窗口的核心方法&#xff0c;具有以下关键特性&#xff1a; 浮动窗口&#xff1a;浮于普通 GUI 控件之上焦点控制&#xff1a;可通过点击获得焦…