1 概述

测试用例的编写要有一些基础的规范,在本文先定义文件名称和测试用例方法名的规范。

2 文件加载原理

先从源码来看一下测试用例的文件加载原理。

2.1 文件的匹配

主要是通过注解来扫描测试用例。

// 在IDEA测试用例启动时,调用junit-platform-launcher-x.x.x.jar包里的类启动,最终会调JupiterTestEngine.discover()来加载测试用例
// 估计在不同的地方,启动的方式可能有所不同,junit-platform-launcher-x.x.x.jar包并没有引入到工程里
// 源码位置:org.junit.jupiter.engine.JupiterTestEngine
public TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {JupiterConfiguration configuration = new CachingJupiterConfiguration(new DefaultJupiterConfiguration(discoveryRequest.getConfigurationParameters()));JupiterEngineDescriptor engineDescriptor = new JupiterEngineDescriptor(uniqueId, configuration);// 1. 先在DiscoverySelectorResolver()初始化resolver,然后使用resolver去加载测试用例new DiscoverySelectorResolver().resolveSelectors(discoveryRequest, engineDescriptor);return engineDescriptor;
}// 源码位置:org.junit.jupiter.engine.discovery.DiscoverySelectorResolver
public class DiscoverySelectorResolver {// 初始化多个resolverprivate static final EngineDiscoveryRequestResolver<JupiterEngineDescriptor> resolver = EngineDiscoveryRequestResolver.<JupiterEngineDescriptor>builder()// 2. 设置加载测试用例文件的ClassContainerSelectorResolver,IsTestClassWithTests是最终的过滤类ClassFilter.addClassContainerSelectorResolver(new IsTestClassWithTests()) .addSelectorResolver(context -> new ClassSelectorResolver(context.getClassNameFilter(), context.getEngineDescriptor().getConfiguration())).addSelectorResolver(context -> new MethodSelectorResolver(context.getEngineDescriptor().getConfiguration())).addTestDescriptorVisitor(context -> new ClassOrderingVisitor(context.getEngineDescriptor().getConfiguration())).addTestDescriptorVisitor(context -> new MethodOrderingVisitor(context.getEngineDescriptor().getConfiguration())).addTestDescriptorVisitor(context -> TestDescriptor::prune).build();public void resolveSelectors(EngineDiscoveryRequest request, JupiterEngineDescriptor engineDescriptor) {resolver.resolve(request, engineDescriptor);}
}// 源码位置:org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver.Builder
public Builder<T> addClassContainerSelectorResolver(Predicate<Class<?>> classFilter) {Preconditions.notNull(classFilter, "classFilter must not be null");// 3. 把ClassFilter包到ClassContainerSelectorResolver里面,这个ClassFilter是IsTestClassWithTestsreturn addSelectorResolver(context -> new ClassContainerSelectorResolver(classFilter, context.getClassNameFilter()));
}
public Builder<T> addSelectorResolver(Function<InitializationContext<T>, SelectorResolver> resolverCreator) {// 4. ClassContainerSelectorResolver最终存储在resolverCreators变量里resolverCreators.add(resolverCreator);return this;
}// 回到DiscoverySelectorResolver,调用resolveSelectors()
// 源码位置:org.junit.jupiter.engine.discovery.DiscoverySelectorResolver
public class DiscoverySelectorResolver {// 初始化多个resolverprivate static final EngineDiscoveryRequestResolver<JupiterEngineDescriptor> resolver = EngineDiscoveryRequestResolver.<JupiterEngineDescriptor>builder()// 2. 设置加载测试用例文件的ClassContainerSelectorResolver,IsTestClassWithTests是最终的过滤类ClassFilter.addClassContainerSelectorResolver(new IsTestClassWithTests()) .addSelectorResolver(context -> new ClassSelectorResolver(context.getClassNameFilter(), context.getEngineDescriptor().getConfiguration())).addSelectorResolver(context -> new MethodSelectorResolver(context.getEngineDescriptor().getConfiguration())).addTestDescriptorVisitor(context -> new ClassOrderingVisitor(context.getEngineDescriptor().getConfiguration())).addTestDescriptorVisitor(context -> new MethodOrderingVisitor(context.getEngineDescriptor().getConfiguration())).addTestDescriptorVisitor(context -> TestDescriptor::prune).build();public void resolveSelectors(EngineDiscoveryRequest request, JupiterEngineDescriptor engineDescriptor) {5. 执行resolver.resolve加载测试用例resolver.resolve(request, engineDescriptor);}
}// 源码位置:org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver
public void resolve(EngineDiscoveryRequest request, T engineDescriptor) {Preconditions.notNull(request, "request must not be null");Preconditions.notNull(engineDescriptor, "engineDescriptor must not be null");InitializationContext<T> initializationContext = new DefaultInitializationContext<>(request, engineDescriptor);// 6. 初始化resolver,把resolverCreators作为参数传入List<SelectorResolver> resolvers = instantiate(resolverCreators, initializationContext);List<TestDescriptor.Visitor> visitors = instantiate(visitorCreators, initializationContext);new EngineDiscoveryRequestResolution(request, engineDescriptor, resolvers, visitors).run();
}// 源码位置:org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver
private <R> List<R> instantiate(List<Function<InitializationContext<T>, R>> creators, InitializationContext<T> context) {// 7. 遍历Resolver执行lambda表达式函数,这个函数执行的时候,就是执行Resolver的resolve接口,//    其中重点在于ClassContainerSelectorResolver,里面放了IsTestClassWithTests这个ClassFilter,通过该类进行过滤return creators.stream().map(creator -> creator.apply(context)).collect(toCollection(ArrayList::new));
}// 回到EngineDiscoveryRequestResolver
// 源码位置:org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver
public void resolve(EngineDiscoveryRequest request, T engineDescriptor) {Preconditions.notNull(request, "request must not be null");Preconditions.notNull(engineDescriptor, "engineDescriptor must not be null");InitializationContext<T> initializationContext = new DefaultInitializationContext<>(request, engineDescriptor);// 6. 初始化resolver,把resolverCreators作为参数传入List<SelectorResolver> resolvers = instantiate(resolverCreators, initializationContext);List<TestDescriptor.Visitor> visitors = instantiate(visitorCreators, initializationContext);// 8. 执行resolvernew EngineDiscoveryRequestResolution(request, engineDescriptor, resolvers, visitors).run();
}// 源码位置:org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution
void run() {remainingSelectors.addAll(request.getSelectorsByType(DiscoverySelector.class));while (!remainingSelectors.isEmpty()) {// 9. 执行,参数为ClasspathRootSelector,里面放了测试用例class文件的根目录,如果是IDEA环境则是target/test-classes目录resolveCompletely(remainingSelectors.poll());}visitors.forEach(engineDescriptor::accept);
}
private void resolveCompletely(DiscoverySelector selector) {EngineDiscoveryListener discoveryListener = request.getDiscoveryListener();UniqueId engineId = engineDescriptor.getUniqueId();try {// 10. 执行,参数为ClasspathRootSelectorOptional<Resolution> result = resolve(selector);if (result.isPresent()) {discoveryListener.selectorProcessed(engineId, selector, resolved());enqueueAdditionalSelectors(result.get());}else {discoveryListener.selectorProcessed(engineId, selector, unresolved());}}catch (Throwable t) {UnrecoverableExceptions.rethrowIfUnrecoverable(t);discoveryListener.selectorProcessed(engineId, selector, failed(t));}
}// 源码位置:org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution
private Optional<Resolution> resolve(DiscoverySelector selector) {if (resolvedSelectors.containsKey(selector)) {return Optional.of(resolvedSelectors.get(selector));}if (selector instanceof UniqueIdSelector) {return resolveUniqueId((UniqueIdSelector) selector);}// 11. 执行resolver,重点关注ClassContainerSelectorResolverreturn resolve(selector, resolver -> {Context context = getContext(selector);if (selector instanceof ClasspathResourceSelector) {return resolver.resolve((ClasspathResourceSelector) selector, context);}if (selector instanceof ClasspathRootSelector) {return resolver.resolve((ClasspathRootSelector) selector, context);}if (selector instanceof ClassSelector) {return resolver.resolve((ClassSelector) selector, context);}if (selector instanceof NestedClassSelector) {return resolver.resolve((NestedClassSelector) selector, context);}if (selector instanceof DirectorySelector) {return resolver.resolve((DirectorySelector) selector, context);}if (selector instanceof FileSelector) {return resolver.resolve((FileSelector) selector, context);}if (selector instanceof MethodSelector) {return resolver.resolve((MethodSelector) selector, context);}if (selector instanceof NestedMethodSelector) {return resolver.resolve((NestedMethodSelector) selector, context);}if (selector instanceof ModuleSelector) {return resolver.resolve((ModuleSelector) selector, context);}if (selector instanceof PackageSelector) {return resolver.resolve((PackageSelector) selector, context);}if (selector instanceof UriSelector) {return resolver.resolve((UriSelector) selector, context);}return resolver.resolve(selector, context);});
}
private Optional<Resolution> resolve(DiscoverySelector selector,Function<SelectorResolver, Resolution> resolutionFunction) {// 12. resolutionFunction是第11步的lambda表达式return resolvers.stream().map(resolutionFunction).filter(Resolution::isResolved).findFirst() // 触发lambda表达式执行.map(resolution -> {contextBySelector.remove(selector);resolvedSelectors.put(selector, resolution);resolution.getMatches().forEach(match -> resolvedUniqueIds.put(match.getTestDescriptor().getUniqueId(), match));return resolution;});
}// 源码位置:org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution
private Optional<Resolution> resolve(DiscoverySelector selector) {if (resolvedSelectors.containsKey(selector)) {return Optional.of(resolvedSelectors.get(selector));}if (selector instanceof UniqueIdSelector) {return resolveUniqueId((UniqueIdSelector) selector);}// 11. 执行resolver,重点关注ClassContainerSelectorResolverreturn resolve(selector, resolver -> {Context context = getContext(selector);if (selector instanceof ClasspathResourceSelector) {return resolver.resolve((ClasspathResourceSelector) selector, context);}if (selector instanceof ClasspathRootSelector) {// 13. 执行resolver的resolve()方法return resolver.resolve((ClasspathRootSelector) selector, context);}if (selector instanceof ClassSelector) {return resolver.resolve((ClassSelector) selector, context);}if (selector instanceof NestedClassSelector) {return resolver.resolve((NestedClassSelector) selector, context);}if (selector instanceof DirectorySelector) {return resolver.resolve((DirectorySelector) selector, context);}if (selector instanceof FileSelector) {return resolver.resolve((FileSelector) selector, context);}if (selector instanceof MethodSelector) {return resolver.resolve((MethodSelector) selector, context);}if (selector instanceof NestedMethodSelector) {return resolver.resolve((NestedMethodSelector) selector, context);}if (selector instanceof ModuleSelector) {return resolver.resolve((ModuleSelector) selector, context);}if (selector instanceof PackageSelector) {return resolver.resolve((PackageSelector) selector, context);}if (selector instanceof UriSelector) {return resolver.resolve((UriSelector) selector, context);}return resolver.resolve(selector, context);});
}// 源码位置:org.junit.platform.engine.support.discovery.ClassContainerSelectorResolver
public Resolution resolve(ClasspathRootSelector selector, Context context) {return classSelectors(findAllClassesInClasspathRoot(selector.getClasspathRoot(), classFilter, classNameFilter));
}
private SelectorResolver.Resolution classSelectors(List<Class<?>> classes) {return classes.isEmpty() ? Resolution.unresolved() : Resolution.selectors((Set)classes.stream().map(DiscoverySelectors::selectClass).collect(Collectors.toSet()));
}
public static List<Class<?>> findAllClassesInClasspathRoot(URI root, Predicate<Class<?>> classFilter, Predicate<String> classNameFilter) {// 14. classFilter即前面提供的IsTestClassWithTests,查找指定目录(root)的类文件,并通过classFilter过滤//     ReflectionUtils里使用classpathScanner扫描测试用例文件,调用classFilter的test()方法进行过滤return ReflectionUtils.findAllClassesInClasspathRoot(root, classFilter, classNameFilter);
}
// 源码位置:org.junit.platform.commons.util.ReflectionUtils
public static List<Class<?>> findAllClassesInClasspathRoot(URI root, Predicate<Class<?>> classFilter, Predicate<String> classNameFilter) {return findAllClassesInClasspathRoot(root, ClassFilter.of(classNameFilter, classFilter));
}
public static List<Class<?>> findAllClassesInClasspathRoot(URI root, ClassFilter classFilter) {return Collections.unmodifiableList(classpathScanner.scanForClassesInClasspathRoot(root, classFilter));
}// 源码位置:org.junit.jupiter.engine.discovery.predicates.IsTestClassWithTests
public class IsTestClassWithTests implements Predicate<Class<?>> {// 根据方法上有@Test注解过滤private static final IsTestMethod isTestMethod = new IsTestMethod();// 根据有@TestFactory注解过滤private static final IsTestFactoryMethod isTestFactoryMethod = new IsTestFactoryMethod();// 根据有@TestTemplate注解过滤private static final IsTestTemplateMethod isTestTemplateMethod = new IsTestTemplateMethod();// 15. 执行过滤public boolean test(Class<?> candidate) {return isPotentialTestContainer.test(candidate)&& (hasTestOrTestFactoryOrTestTemplateMethods(candidate) || hasNestedTests(candidate));}// 省略其它代码
}// 源码位置:org.junit.jupiter.engine.discovery.predicates.IsTestMethod
public class IsTestMethod extends IsTestableMethod {public IsTestMethod() {super(Test.class, true); // @Test注解}
}
// 源码位置:org.junit.jupiter.engine.discovery.predicates.IsTestFactoryMethod
public class IsTestFactoryMethod extends IsTestableMethod {public IsTestFactoryMethod() {super(TestFactory.class, false); //@TestFactory注解}
}
// 源码位置:org.junit.jupiter.engine.discovery.predicates.IsTestTemplateMethod
public class IsTestTemplateMethod extends IsTestableMethod {public IsTestTemplateMethod() {super(TestTemplate.class, true); // @TestTemplate注解}
}// org.junit.jupiter.engine.discovery.predicates.IsTestableMethod
abstract class IsTestableMethod implements Predicate<Method> {private final Class<? extends Annotation> annotationType;private final boolean mustReturnVoid;IsTestableMethod(Class<? extends Annotation> annotationType, boolean mustReturnVoid) {this.annotationType = annotationType;this.mustReturnVoid = mustReturnVoid;}@Overridepublic boolean test(Method candidate) {// Please do not collapse the following into a single statement.if (isStatic(candidate)) { // 不支持静态方法的测试用例return false;}if (isPrivate(candidate)) { // 不支持私有方法的测试用例return false;}if (isAbstract(candidate)) { // 不支持抽象方法的测试用例return false;}if (returnsVoid(candidate) != this.mustReturnVoid) { // 不支持返回值不是Void的测试用例return false;}// 16. 根据注解过滤return isAnnotated(candidate, this.annotationType);}
}// org.junit.platform.commons.util.AnnotationUtils
public static boolean isAnnotated(AnnotatedElement element, Class<? extends Annotation> annotationType) {// 17. 查找对应的注解,如果注解存在则是满足要求的测试用例类//     即带有@Test、@TestFactory、@TestTemplate这三种注解的测试用例类才是符合要求的测试用例类return findAnnotation(element, annotationType).isPresent();
}// 回到EngineDiscoveryRequestResolution,存储找到的测试用例
// 源码位置:org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution
private void resolveCompletely(DiscoverySelector selector) {EngineDiscoveryListener discoveryListener = request.getDiscoveryListener();UniqueId engineId = engineDescriptor.getUniqueId();try {// 10. 执行,参数为ClasspathRootSelectorOptional<Resolution> result = resolve(selector);if (result.isPresent()) {discoveryListener.selectorProcessed(engineId, selector, resolved());// 17. 把找到的测试用例类存到变量中,供后面使用enqueueAdditionalSelectors(result.get());}else {discoveryListener.selectorProcessed(engineId, selector, unresolved());}}catch (Throwable t) {UnrecoverableExceptions.rethrowIfUnrecoverable(t);discoveryListener.selectorProcessed(engineId, selector, failed(t));}
}

2.2 规则

从上面原理看,可以分为文件和方法两个角度来分析。

从文件角度来说,得有@Test、@TestFactory、@TestTemplate三种注解中的一种,测试用例文件才会被加载到。@TestFactory是用于动态创建多个测试用例,感觉测试用例还是一个个分开写比较容易维护,此注解争取不使用。

@TestTemplate注解则可以帮助提供动态参数给测试用例,在某些场景可能需要用,建议封装到框架里使用。

从方法角度来看,私有方法、静态方法、抽象方法、有返回值的方法,都不能成为测试用例,需要规避这些情况。

@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {@Autowiredprivate MockMvc mockMvc;@Testpublic void should_say_string() throws Exception {String messge = "abc";MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post("/sayHello").contentType(MediaType.APPLICATION_FORM_URLENCODED).content("message=" + messge)).andExpect(status().isOk()).andReturn();assertThat(result.getResponse().getContentAsString()).isEqualTo("Hello world: " + messge);}@Testprivate String check_empty_in_private_method() {String str = "abc";assertThat(str).isNotEmpty();return str;}@Testpublic String check_empty_with_return_value() {String str = "abc";assertThat(str).isNotEmpty();return str;}@Testpublic static void check_empty_in_static_method() {String str = "abc";assertThat(str).isNotEmpty();}
}

3 架构一小步

规范:测试用例文件名用xxxTest.java为后缀。不使用Tests.java后缀,避免容易漏。

规范:测试用例方法都加上@Test注解,需要动态参数的地方用@TestTemplate,尽量不用@TestFactory。

规范:测试用例方法名使用下划线的方式,用表意的方式描述清楚测试用例的用途,长度可以长一些。

规范:测试用例方法名不需要指定test关键字。

规范:测试用例方法必须为public、非静态、非抽象、无返回值方法。

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

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

相关文章

K8S的CNI之calico插件升级至3.30.2

前言宿主机ping不通K8S的pod&#xff0c;一直存在丢包的现象&#xff0c;排查了防火墙、日志、详细信息等没发现什么问题&#xff0c;最后搜索发现&#xff0c;是因为把K8S的版本升级之后&#xff0c;旧版本的CNI插件不适配原因导致的&#xff0c;于是就把calico也一并升级并且…

Spring Boot RESTful API 设计指南:查询接口规范与最佳实践

Spring Boot RESTful API 设计指南&#xff1a;查询接口规范与最佳实践 引言 在 Spring Boot 开发中&#xff0c;查询接口的设计直接影响着系统的可用性、可维护性和性能。本文将深入探讨如何规范设计查询接口&#xff0c;包括 GET/POST 的选择、参数定义、校验规则等&#xff…

ctfshow萌新题集

记录一下前半部分是能自己写出来的&#xff0c;后半部分是需要提示的&#xff0c;感觉自己归来两年仍是萌新 misc部分 知识点 base家族密文特征 Base16 (Hex) 字符集&#xff1a;0-9, A-F&#xff08;不区分大小写&#xff09;。特征&#xff1a; 长度是 2 的倍数&#xff…

2025年语言处理、大数据与人机交互国际会议(DHCI 2025)

&#x1f310;&#x1f916;&#x1f9e0; 语言处理、大数据与人机交互&#xff1a;探索智能未来 —— DHCI 2025国际会议2025年语言处理、大数据与人机交互国际会议&#xff08;DHCI 2025&#xff09; 将于2025年在中国重庆市召开。这次盛会将汇聚全球顶尖专家、学者及行业领袖…

RIP实验以及核心原理

RIP&#xff08;Routing Information Protocol&#xff0c;路由信息协议&#xff09;是一种内部网关协议&#xff0c;基于距离矢量算法&#xff0c;用于在自治系统内交换路由信息。RIP 核心原理距离矢量算法&#xff1a;RIP 使用跳数作为路径选择的唯一度量标准。每经过一个路由…

基于大数据的电力系统故障诊断技术研究

摘要本文提出了一种创新性的基于大数据技术的电力系统故障诊断方法&#xff0c;该方法通过整合先进的机器学习算法和交互式可视化技术&#xff0c;实现了对电力系统各类故障的智能化识别与深度分析。该系统采用随机森林算法作为核心分类器&#xff0c;构建了高精度的故障分类模…

MySQL 分区功能应用专门实现全方位详解与示例

MySQL 分区功能允许将表的数据分散存储在不同的物理分区中,同时保持逻辑上的单一表结构。下面我将从基础概念到高级应用,全面讲解 MySQL 分区实现。 一、分区核心作用 1. 性能提升 分区剪枝(Partition Pruning):查询时自动跳过不相关的分区,减少数据扫描量 并行处理:不…

汽车功能安全-嵌入式软件测试(软件合格性测试)【目的、验证输入、集成验证要求】11

文章目录1 嵌入式软件测试&#xff08;Testing of the embedded Software&#xff09;2 测试输入3 验证要求和建议3.1 测试环境3.2 测试方法3.2.1 基于需求的测试3.2.2 故障注入测试3.2.3 两种方法的区别与联系总结3.3 测试用例导出方法4 嵌入式软件的测试结果评价5 测试输出物…

【webrtc】gcc当前可用码率1:怎么决策的

【webrtc】当前最大码率是怎么决策的1 看日志,跟踪代码最大码率 是probe的上限 默认值很大 外部设置的较小,调用堆栈 无限大作为默认值 默认是无限大,所以使用预设值 【webrtc】码率设定中的 int64_t 的无限大

UE5 C++计时器

UE5 C计时器 计时器一&#xff1a; .h文件 FTimerHandle TimerHandle_BetweenShot;//定义时间句柄 void StartFire();void EndFire();.cpp文件 #include “TimerManager.h” void ASpaceShip::StartFire() {GetWorldTimerManager().SetTimer(TimerHandle_BetweenShot, this, &a…

【hivesql 已知维度父子关系加工层级表】

这里写自定义目录标题1. 维度表示例1.1清单表1.2层级表2.从清单表加工层级表2.1 注意点2.2 加工方式&#xff08;join&#xff09;2.3 使用函数3.清单表字段加工3.1通过上级编码信息加工级别信息3.2 通过级别信息&#xff0c;加工上级编码信息4.创建维度表的一般注意点1. 维度表…

Ubuntu重装系统后ssh连接不上(遇到 ​​“Unit ssh.service not found“​​ 错误)

重装系统时不知道为什么SSH 服务未安装&#xff0c;以下是解决方案&#xff1a;先检查ssh服务安装没安装 sudo systemctl status ssh # Ubuntu/Debian如果 systemctl 找不到服务&#xff0c;可能是 SSH 未安装&#xff1a;sudo apt update sudo apt install openssh-serve…

2025社交电商新风口:推客小程序的商业逻辑与技术实现

一、推客小程序市场前景与商业价值在当今社交电商蓬勃发展的时代&#xff0c;推客小程序已成为连接商家与消费者的重要桥梁。推客模式结合了社交传播与电商变现的双重优势&#xff0c;通过用户自发分享带来裂变式增长&#xff0c;为商家创造了全新的营销渠道。推客小程序的核心…

Go 单元测试进阶:AI 加持下的高效实践与避坑指南

单元测试的必要性与基础单元测试不仅是保障代码质量的手段&#xff0c;也是优秀的设计工具和文档形式&#xff0c;对软件开发具有重要意义。另一种形式的文档&#xff1a;好的单元测试是一种活文档&#xff0c;能清晰展示代码单元的预期用途和行为&#xff0c;有时比注释更有用…

VScode SSH远程连接Ubuntu(通过SSH密钥对的方式)

我们都知道在VScode上通过SSH插件的方式可以远程连接到虚拟机的Ubuntu系统&#xff0c;这样开发者就可以在Windows下的Vscode编译器下直接远程连接Ubuntu&#xff0c;这种方式是 “用 Windows 的便捷性操作 Linux 的专业性”—— 既保留了Windows系统的易用性和VS Code的强大功…

学术绘图(各种神经网络)

23种神经网络设计&可视化工具汇总 下面做简要罗列&#xff0c;具体请看相关链接 1.draw_convnet Github: https://github.com/gwding/draw_convnet​ star 数量&#xff1a;1.7k​ 这个工具最后一次更新是2018年的时候&#xff0c;一个Python脚本来绘制卷积神经网络的工…

Redis的高可用性与集群架构

Redis的高可用性与集群架构 引言&#xff1a;解释高可用性的重要性及Redis如何实现主从复制&#xff08;Replication&#xff09; 原理&#xff1a;异步复制&#xff0c;主从数据同步配置方法优缺点分析 哨兵模式&#xff08;Sentinel&#xff09; 功能&#xff1a;监控、通知、…

TCP的连接

TCP 三次握手过程是怎样的&#xff1f;TCP 是面向连接的协议&#xff0c;所以使用 TCP 前必须先建立连接&#xff0c;而建立连接是通过三次握手来进行的。三次握手的过程如下图&#xff1a;一开始&#xff0c;客户端和服务端都处于 CLOSE 状态。先是服务端主动监听某个端口&…

Excel的学习

一、熟悉界面 1.功能区 点击“视图”,点击冻结窗格,选择目标行 2.表格区 3.自定义功能区 在上面的空白编辑栏处,右键选择自定义功能区 4.数据输入规范 (1)格式不统一(日期格式不规范,姓名乱加空格,乱合并单元格) 姓名对齐:右键选择编辑单元格格式,选择对齐,…