当前位置: 首页 > news >正文

单元测试之Mockito使用

测试中为什么需要Mock

在实际的测试中,被测试的对象并不都是可以通过简单的new操作符就可以创建出来的!

实际的业务代码中,一个业务类往往要依赖很多其他的类才能完成一个完整的业务方法,这些依赖包括第三方的rpc,db的查询等等,具体的拓扑如下图

我们想测试ClassA,但是ClassA依赖ClassB和ClassC,其中ClassB又依赖ClassD和ClassE,我们要初始化ClassA,需要构造5个类,除了ClassA直接依赖的ClassB和ClassC的部分方法的返回值外,其他的4个类的逻辑对我们测试ClassA方法没有任何帮助,都是累赘。

Mockito提供了一种非常简单的方式,让我们Mock被测试类的依赖,让更多的精力聚集到真正的测试逻辑上,也不是构造初始化依赖类上。

添加Maven依赖

<dependencies><!-- Mockito 核心依赖--><dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>${mockito.version}</version><scope>test</scope></dependency><!-- Mockito 的 JUnit 5 扩展--><dependency><groupId>org.mockito</groupId><artifactId>mockito-junit-jupiter</artifactId><version>${mockito.version}</version><scope>test</scope></dependency>
</dependencies>

mockito-core的作用

  • 创建模拟对象:通过 Mockito.mock() 或 @Mock 注解创建接口或类的模拟实例。
  • 存根方法行为:定义模拟对象的方法返回值或异常(如 when(...).thenReturn(...))。
  • 验证交互:检查模拟对象的方法是否被调用(如 verify(mock).method())。

mockito-junit-jupiter的作用

  • 自动初始化模拟对象:通过 @ExtendWith(MockitoExtension.class) 自动创建 @Mock 和 @InjectMocks 注解标记的实例。
  • 依赖注入:将 @Mock 对象自动注入到 @InjectMocks 标记的待测类中。
  • 减少样板代码:无需手动调用 MockitoAnnotations.openMocks(this)

主要概念及使用模版

概念

  • 被测试类:我们最应该关注的类,我们测试的对象
  • 依赖类:被测试类的依赖类,大部分都是需要我们mock掉的类,要不就是太难构造,要不就是没有稳定的返回值,要不就是跟我们测试关系不大
  • 存根stub:告诉Mockito被mock的方法,在接收到不同的参数时,返回什么的返回值
  • mock对象:Mockito使用mock方式mock的依赖类,不对方法进行stub时,调用mock方法返回默认值/null
  • spy对象:Mockito使用spy方式mock的依赖类,不对方法进行stub时,调用mock方法执行真实逻辑

如Mockito官网所说:

使用 Mockito 只能做两件事 - 存根stub或验证verify 。存根在执行之前进行,验证在执行之后进行。

使用Mockito后的测试模版

  1. mock依赖类
  2. 新建被测试对象,并把mock对象注入进去
  3. 对测试需要的mock对象的方法进行stub存根
  4. 调用被测试的方法
  5. 对结果进行断言
  6. 对mock对象的方法进行行为验证verify、对mock对象方法的入参(参数捕获)进行断言

使用Mockito

代码准备

// 被测试类
class UserService {private UserRepository userRepository;public UserService(UserRepository userRepository) {this.userRepository = userRepository;}public String getUserName(Long userId) {return userRepository.findNameById(userId);}
}// 依赖接口
interface UserRepository {String findNameById(Long userId);
}

 

一、二、Mock依赖类并注入

对于模版中的第1步和第2步,Mockito支持了三种方式

1. API方式,并手动注入

class UserServiceTest {@Testvoid testGetUserName() {// 1. mock依赖类, 此处已mock方式为例,spy也是同样方式(特别注意spy只能接受非空对象,此处是个接口使用spy会报错,只是举个小栗子)UserRepository userRepositoryMock = Mockito.mock(UserRepository.class);// 2. 新建被测试对象,并把mock对象注入进去,此处使用构造方法注入,当然也可以使用setter注入UserService userService = new UserService(userRepositoryMock);// 剩下的步骤// .....
    }
}

2. 注解+MockitoAnnotations.openMocks(this) 

class UserServiceTest {/*** 1. mock依赖类, 此处已@Mock注解为例,@Spy也是同样方式* @Mock和Mockito.mock()等效* @Spy和Mockito.spy()等效(特别注意spy只能接受非空对象,此处是个接口使用spy会报错,只是举个小栗子)*/@Mockprivate UserRepository userRepositoryMock;/*** 2. 使用@InjectMocks会自动新建被测试对象,并把被@Mock/@Spy修饰的对象注入进去*/@InjectMocksprivate UserService userService;private AutoCloseable autoCloseable;@BeforeEachvoid setUp() {// 初始化Mock对象,手动开启注解autoCloseable = MockitoAnnotations.openMocks(this);}@AfterEachvoid tearDown() throws Exception {// 关闭Mock对象,手动关闭新建的对象
        autoCloseable.close();}@Testvoid testGetUserName() {// 剩下的步骤// .....
    }
}

3. 注解+扩展@ExtendWith(MockitoExtension.class) (推荐)

@ExtendWith(MockitoExtension.class) // 自动初始化Mock并关闭
class UserServiceTest {/*** 1. mock依赖类, 此处已@Mock注解为例,@Spy也是同样方式* @Mock和Mockito.mock()等效* @Spy和Mockito.spy()等效(特别注意spy只能接受非空对象,此处是个接口使用spy会报错,只是举个小栗子)*/@Mockprivate UserRepository userRepositoryMock;/*** 2. 使用@InjectMocks会自动新建被测试对象,并把被@Mock/@Spy修饰的对象注入进去*/@InjectMocksprivate UserService userService;@Testvoid testGetUserName() {// 剩下的步骤// .....
    }
}

关于mock和spy的进一步说明

静态方法的mock

下面是MockedStatic的使用例子,使用mockConstruction()新建MockedConstruction进行构造方法的mock也是同理哈,就不赘述了

1. API方式

MockedStatic是AutoCloseable的子类,此处使用了try-with-resources语句来处理, 当然你也可以在@BeforeEach中调用 Mockito.mockStatic(XXClass.class) ,在@AfterEach里进行关闭 mockedStatic.close(); 

class UserServiceTest {@Testvoid testGetUserName() {// 1. mock依赖类, 此处已mock方式为例,spy也是同样方式(特别注意spy只能接受非空对象,此处是个接口使用spy会报错,只是举个小栗子)UserRepository userRepositoryMock = Mockito.mock(UserRepository.class);// 2. 新建被测试对象,并把mock对象注入进去,此处使用构造方法注入,当然也可以使用setter注入UserService userService = new UserService(userRepositoryMock);// 2.1 对需要的静态方法进行mocktry (MockedStatic<XXClass> mockedStatic = Mockito.mockStatic(XXClass.class)) {// 2.2 对需要的静态方法进行stubmockedStatic.when(() -> XXClass.getXXX()).thenReturn(YYY);// 剩下的步骤// .....
        }}
}

2. 使用@Mock注解(推荐)

使用注解时,需要配置MockitoAnnotations.openMocks(this)或者@ExtendWith(MockitoExtension.class)一起使用,MockedStatic的关闭就可以交给Mockito自己管理

class UserServiceTest {@MockMockedStatic<XXClass> mockedStatic;@Testvoid testGetUserName() {// 剩下的步骤// .....
    }
}

三、存根Stub

对于模版里的第三步就简单多了,按着Mockito的javaDoc来用就OK了

简单例子

 //You can mock concrete classes, not just interfacesLinkedList mockedList = mock(LinkedList.class);//stubbingwhen(mockedList.get(0)).thenReturn("first");when(mockedList.get(1)).thenThrow(new RuntimeException());//following prints "first"System.out.println(mockedList.get(0));//following throws runtime exceptionSystem.out.println(mockedList.get(1));//following prints "null" because get(999) was not stubbedSystem.out.println(mockedList.get(999));

连续存根

 when(mock.someMethod("some arg")).thenThrow(new RuntimeException()).thenReturn("foo");//First call: throws runtime exception:mock.someMethod("some arg");//Second call: prints "foo"System.out.println(mock.someMethod("some arg"));//Any consecutive call: prints "foo" as well (last stubbing wins).System.out.println(mock.someMethod("some arg"));// 或者使用下面方式
when(mock.someMethod("some arg")).thenReturn("one", "two", "three");

特别注意: 连续存储或多次存更效果是不一样的, 下面这种多次存根,后面的存根会覆盖前面的存根,每次调用都返回最后一次

 //All mock.someMethod("some arg") calls will return "two"when(mock.someMethod("some arg")).thenReturn("one")when(mock.someMethod("some arg")).thenReturn("two")

上面的方式都需要有个返回值,那void方法应该怎么存根呢? 可以使用doXXX方式

doXXX方法进行存根

You can use doThrow(), doAnswer(), doNothing(), doReturn() and doCallRealMethod() in place of the corresponding call with when(), for any method. It is necessary when you

stub void methods
stub methods on spy objects (see below)
stub the same method more than once, to change the behaviour of a mock in the middle of a test.

参数匹配器

在存根时或者验证方法时,有时不关心方法的入参是什么,此时就可以使用参数匹配器了

 //stubbing using built-in anyInt() argument matcherwhen(mockedList.get(anyInt())).thenReturn("element");//stubbing using custom matcher (let's say isValid() returns your own matcher implementation):when(mockedList.contains(argThat(isValid()))).thenReturn(true);//following prints "element"System.out.println(mockedList.get(999));//you can also verify using an argument matcher
 verify(mockedList).get(anyInt());//argument matchers can also be written as Java 8 Lambdasverify(mockedList).add(argThat(someString -> someString.length() > 5));

其他类型详见: ArgumentMatchers.anyXXX()方法

函数名匹配类型
any() 所有对象类型
anyInt() 基本类型 int、非 null 的 Integer 类型
anyChar() 基本类型 char、非 null 的 Character 类型
anyShort() 基本类型 short、非 null 的 Short 类型
anyBoolean() 基本类型 boolean、非 null 的 Boolean 类型
anyDouble() 基本类型 double、非 null 的 Double 类型
anyFloat() 基本类型 float、非 null 的 Float 类型
anyLong() 基本类型 long、非 null 的 Long 类型
anyByte() 基本类型 byte、非 null 的 Byte 类型
anyString() String 类型(不能是 null)
anyList() List<T> 类型(不能是 null)
anyMap() Map<K, V>类型(不能是 null)
anyCollection() Collection<T>类型(不能是 null)
anySet() Set<T>类型(不能是 null)
any(Class<T> type) type类型的对象(不能是 null)
isNull() null
notNull() 非 null
isNotNull() 非 null

 

四、调用被测试方法

构造入参,调用被测试类的方法进行测试吧

五、对结果断言

断言使用

六、行为验证Verify

当测试对象 A 依赖 B(B 被 Mock),而 A 的内部逻辑间接调用了 B 的方法时,需通过 verify() 验证 B 是否被正确调用。

是对 被mock的对象的方法 行为的验证,最常用的为:调用次数

// 基础用法:验证方法是否被调用
verify(mockObject).methodName();// 验证调用次数
verify(mockObject, times(n)).methodName(); // 精确 n 次
verify(mockObject, never()).methodName();  // 从未调用
verify(mockObject, atLeastOnce()).methodName(); // 至少一次

还可以验证方法的调用顺序

InOrder inOrder = inOrder(mockRepo);
inOrder.verify(mockRepo).save("Alice");
inOrder.verify(mockRepo).flush();

参数捕获

创建参数捕获器有两种方

// 声明要捕获的参数类型
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);// 或者使用注解方式
@Captor
ArgumentCaptor<User> msgArgumentCaptor;

 

在验证时捕获参数

// 单次捕获
verify(userRepository).save(userCaptor.capture());
// 获得参数并验证
User capturedUser = userCaptor.getValue();
assertThat(capturedUser.getName()).isEqualTo("Alice");
assertThat(capturedUser.getAge()).isEqualTo(30);

如果被验证的方法调用了多次,就可以获得捕获的多个参数

// 捕获所有调用参数
verify(mockRepo, times(3)).save(userCaptor.capture());
List<User> allUsers = userCaptor.getAllValues();assertThat(allUsers).extracting(User::getName).containsExactly("Alice", "Bob", "Charlie");

 

参考

mockito的wiki: https://github.com/mockito/mockito/wiki

使用方法都在 javadoc中,写的很详细  https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html

mockito中的一篇文章:如何编写好的测试  https://github.com/mockito/mockito/wiki/How-to-write-good-tests

https://pdai.tech/md/develop/ut/dev-ut-x-mockito.html

 
 
 
 

相关文章:

单元测试之Mockito使用

测试中为什么需要Mock 在实际的测试中,被测试的对象并不都是可以通过简单的new操作符就可以创建出来的! 实际的业务代码中,一个业务类往往要依赖很多其他的类才能完成一个完整的业务方法,这些依赖包括第三方的rpc,db的查询等等,具体的拓扑如下图我们想测试ClassA,但是Cl…...

Jetson有Jtop,Linux有Htop,RDK也有Dtop!

作者:SkyXZ CSDN:SkyXZ~-CSDN博客 博客园:SkyXZ - 博客园 本项目基于btop开源项目进行二次开发,旨在为RDK平台提供更强大的系统监控工具。 Linux系统下有Htop可以作为系统监控,英伟达的Jetson也有第三方的Jtop,咱们RDK虽然也提供了hrut_somstatus来查看BPU的使用…...

《原子习惯》-读书笔记4

2025.09.17 Day4 1、 养成习惯的过程可以分为四个简单的步骤:提示、渴求、反应和奖励。2、你的头脑在不断分析你的内外部环境,寻找奖励所在的线索。因为线索是我们已然接近奖励的第一个迹象,它自然会导致人们滋生渴求。3、提示的作用是让你注意到奖励的存在。渴求是想要得到…...

Java学习第二天

数据类型 Java、c++是一种强类型语言要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用,安全性高,速度较慢 弱类型语言:安全性低,速度较快 Java数据类型分为两大类:基本类型、引用类型位:计算机内部数据存储的最小单位,11001100是一个八位二进制数 字节:是…...

Java学习第三天

顺序结构 package Scanner;import java.util.Scanner;public class Demo05 {public static void main(String[] args) {//我们可以输入多个数字,并求其总和与平均数,每输入一个数字用回车确认,通过输入非数字来结束输入并非输出执行结果Scanner scanner=new Scanner(System.…...

Java学习第四天

break continue break在任何循环语句的主体部分,均可用break控制循环的流程。break用于强行退出循环,不执行循环中剩余的语句(break语句也在switch语句中使用) continue语句用在循环语句体中,用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行…...

搜索百科(1):Lucene —— 打开现代搜索世界的第一扇门

大家好,我是 INFINI Labs 的石阳。 这是《搜索百科》专栏系列文章,每天 5 分钟,带你速览一款搜索相关的技术或产品,同时还会带你探索它们背后的技术原理、发展故事及上手体验等。 搜索技术看似专业,但它早已深度融入我们的日常生活。无论是电商搜索、知识检索,还是 AI 语…...

02020308 .NET Core核心基础组件08-结构化日志和集中日志服务

02020308 .NET Core核心基础组件08-结构化日志和集中日志服务...

zookeeper的配置

问题:1.1号和二号虚拟机可以成功启动三号报错 2025-09-17 17:57:46,219 [myid:] - INFO [main:QuorumPeerConfig@133] - Reading configuration from: /export/server/apache-zookeeper-3.5.6-bin/bin/../conf/zoo.cfg 2025-09-17 17:57:46,228 [myid:] - INFO [main:Quorum…...

02020307 .NET Core核心基础组件07-什么是Logging、NLog

02020307 .NET Core核心基础组件07-什么是Logging、NLog...

算法第一周博客

算法第一周博客任务一,搜索大公司内部编码规范,列出本学期编码需遵守的规范 1.程序块要采用缩进风格编写,缩进的空格数为4个,使得层次分明。 2.控制每行最大长度不超120个字符,超过时换行并适当缩进,并且一行通常一个语句。 3.命名标识符(包括变量,函数,结构体等)时,…...

nid修改dbid/dbname

Table of Contents1. 简述 2. 操作步骤说明 3. 操作命令1. 简述2. 操作步骤说明如果配置了DBconsole,需要删除DBconsole对象。之后完全关闭,再启动到mount状态。 nid 命令支持修改dbid 和 dbname两个值。具体操作见之后示例。 但是在操作之前,需要明确是只需要修改dbid 还是同…...

攻防世界-parallel-comparator-200 - xxx

下载后得到是一个c语言文件,用vs创建一个项目后复制代码发现无法打开<pthread.h>,去网上搜了一下发现windows平台配置有点麻烦 于是就去搜索了一下这个的作用,后面分析代码的时候再提。 先看main函数user_string就是用户需要输入一个长度为20的字符串,然后去看能够触…...

Manim实现脉冲闪烁特效

在数学可视化中,脉冲闪烁特效能像聚光灯一样引导观众注意力,突出关键公式、特殊点或重要结论。 本文将介绍如何一步步通过代码来实现这个特效,并通过参数精准控制视觉效果。 1. 实现原理 脉冲闪烁特效的核心是周期性改变发光体的半径和透明度,模拟能量波动的视觉效果。 这个…...

2025.9.17总结

今天主要内容就是在IDEA里编写代码,后端还是springboot,前端vue3,数据库用hbase。 其他的和之前编写的web项目流程都一样。 就是数据库的使用,和数据库的读写,操作不太一样。 还有hbase,创建表,表结构,读写和常规的mysql不一样。在ai帮助下完成创建表,表结构,读写数据…...

office2024安装包下载安装教程(2025最新整理)office2024专业增强版下载安装教程

在当今数字化办公的时代,一款功能强大且稳定的办公软件是提高工作效率的关键。Office 2024 专业增强版作为微软办公软件家族中的佼佼者,集成了多种实用的办公组件,能够满足各类用户在文档处理、数据管理、演示汇报等多方面的需求。本文将为大家详细介绍 Office 2024 专业增强…...

2025竞赛学习资料

2025竞赛学习资料链接1、竞赛资源链接集 2、CSP-J/S资源链接1 3、C++竞赛综合 4、2025年3月GESP认证C++5级判断题真题详解 5、GESP考试集...

C++ 模板参数推导问题小记(模板类的模板构造函数)

本篇主要是为了记录在编写一个模板类的模板构造函数中遇到的初始化问题,以及针对这个问题展开的相关知识整理,文章就以引发这个问题的代码为标题了。 问题代码 在编写一个代表空间点的模板类 point 时,我打算为它添加一个模板构造函数:代码template<typename T, std::si…...

axios两种写法

// 登录 export const login = (data) => {return request.post(/login, data) }// 权限管理列表 get请求需要写在url上面 export const authAdmin = (params) => {return request.get(/auth/admin, { params }) } ///////////////////////////////////////////////////…...

adobe illustrator中使用画笔工具切割图形

001、测试图形 002、选择画笔工具 3、绘制想要切合的形状,然后同时选中这跟线和图形 004、窗口 + 路径查找器 005、 点击分割 006、 点击取消编组 007、 实现图形分割 。...

2025年了,在 Django 之外,Python Web 框架还能怎么选?

前言 大家好,我是曦远~ 前段时间我写过一篇文章 《Django过时了吗?从ASGI到AI时代的思考》,聊到在 AI 时代下,传统全家桶式框架(比如 Django)该如何面对新趋势。 最近在翻新 DjangoStarter 项目的时候,我顺便做了一些调研,发现 Python Web 生态的变化比我想象得还要快。…...

AtCoder Beginner Contest 423

D - Long Waiting 三个优先队列 #include<bits/stdc++.h> using namespace std; #define endl \n #define yes cout << "YES" << endl #define no cout << "NO" << endl #define pii pair<int,int> #define ll long l…...

SRAM和DRAM的特点和区别

SRAM 静态随机存取存储器 基本结构和工作原理:核心单元:SRAM每个cell 由6 个晶体管 组成,形成一个双稳态触发器电路。 工作原理:这种电路结构由两个稳定的状态,分别代表逻辑“0”和逻辑“1”。只要保持通电,状态就会一直保持下去,不需要额外的操作。 读写过程:通过字线…...

xml基本语法

1. xml的基本结构 XML(可扩展标记语言,eXtensible Markup Language)是一种用于存储和传输结构化数据的标记语言,核心特点是自定义标签和严格的语法规则.一个合法的 XML 文档必须包含文档声明和唯一根元素<?xml version="1.0" encoding="UTF-8"?&g…...

Java25新特性

🛠️ 1. 语言特性与开发者体验实例主方法 (Instance Main Methods)​: 支持省略 public static修饰符的 void main()方法,使初学者更易编写第一个Java程序。// 无需显式类声明和public static修饰符 void main() {IO.println("Hello, JDK 25!"); // java.lang.IO …...

Day17多维数组

多维数组可以看成数组的数组,即在数组中在嵌套一个数组 例如二维数组是一个特殊的一维数组,他的每一个元素都是一个一维数组(1,2,3,4,5.......) 格式与一维数组相似:int [] [] a = new int [i] [j]; 举例的二维数组可以看为两行五列的数组 public class ArrayDemo5 {pub…...

C++ lambda 匿名函数

1、基本介绍 C++11 引入的 lambda 匿名函数(Lambda Expression)是一种轻量级的函数对象,可在需要函数的地方直接定义,无需单独声明,极大简化了代码编写(尤其是回调函数、算法谓词等场景)。 基本语法: [capture-list] (parameter-list) mutable noexcept(optional) ->…...

D拼数

include<stdio.h> include<stdlib.h> #include<string.h> int compare(const void *a, const void *b) { char str1[40], str2[40]; // 1:a在前,b在后 sprintf(str1, "%d%d", *(int *)a, *(int *)b); // 2:b前a后 sprintf(str2, "%d%d&q…...

20250917 - WETToken 攻击事件:价格操控产生的套利空间

背景信息Alert:https://x.com/TenArmorAlert/status/1968223320693686423 TX:https://app.blocksec.com/explorer/tx/bsc/0xf92539acf7eadfd4a98925927a52af5349cb13c2a250908373a5baf8ea4b49adTrace 分析 发生攻击的位置在闪电贷的 callback 函数里面,执行完闪电贷后攻击合…...

题解:P6798 「StOI-2」简单的树

简单的树: 题意: 一颗树,每个节点有一个权值 \(c_i\)。 \(val_i\):\(i\) 为根的子树内所有 \(c_i\) 的最大值。 \(f(x,y)\):\(c_{x}\) 改为 \(y\) 后 \(val_i\) 之和。 每次询问给定 \((l,r,a)\) ,求 \(\sum\limits_{i=l}^{r}{f(a,i)}\)。 思路 首先一眼看出来几个性质:…...

题解:P11704 [ROIR 2025] 旅行路线

旅行路线: 很有参考价值的一道题,其他题解有点抽象,我来。 转化题意 题意转化为 \((1,2)→(n-1,m),(2,1)→(n,m-1)\) 的两条链不相交且经过所有关键点的方案数。 其他点没用,我们以下的点指关键点。 无不能相交限制的 DP 由于 \(x_i\le x_j,y_i\le y_j\),\(i\) 才可以转移…...

题解:P11292 【MX-S6-T4】「KDOI-11」彩灯晚会

彩灯晚会:\(n\) 点 \(m\) 边 \(k\) 种颜色,给每个点染色。 \(cnt_i\):第 \(i\) 种颜色长度为 \(l\) 的链的数量。其中 \(l\) 为题目给的一个常量。 求 \(\sum_{染色方案}\sum_{i=1}^k cnt_i^2\) 的和。一\(\sum_{染色方案}cnt_i\) 值都一样,钦定 \(pos\) 作为代表颜色,那么…...

算法课程第一周作业

《数学之美》第一章启示 《数学之美》的第一章,在算法工程师眼中,并非传授某个具体算法.而是重构了我们理解、设计和应用算法的底层思维框架,世界的基本问题是算法问题,而数学是寻找最优算法的终极语言。 启示一:所有问题本质上都是建模与算法选择问题.意味着世界是一个巨大的待…...

实测对比:权威榜单之微信排版Top 5编辑器大揭秘

在新媒体运营的世界里,微信排版可是重中之重,它直接影响着文章的视觉效果和读者的阅读体验。很多运营人都有这样的痛点:写作慢、排版耗时、跨平台排版不统一、配图难还可能有侵权风险等。为了帮大家解决这些难题,我亲测了有一云AI编辑器、智撰AI编辑器等多款主流编辑器。在…...

自建仓库推送到NAS采用 Docker Registry 工作流

放弃手动 `save` 和 `load` 的方式,改用行业标准的 Registry(仓库)模式。这是最专业、最高效的方案。 **优点**: - **彻底解决版本兼容性问题**,因为 push/pull 协议是标准化的。 - 传输效率高,再次推送时只会上传有变动的层(layer)。 - 是 DevOps 和自动化流程的基础,…...

【汇编和指令集 . 第2025 . 9期】发现大牛

【编者按】在计算机、互联网风行半个世纪之后,我们发现:科技预言家越来越多了,思想家缺位了。生活节奏变快了,思想退步了;书写减少了,纸张缺没少;知识泛滥了,思考没有深入......我们有可能遭AI时代的反噬。时代呼唤跨文理的大家,呼唤有温度的电子产品。发刊词: …...

Opencompass避坑日记

安装首先执行pip安装 再下载源代码第一句是为了安装opencompass的依赖包,第二句是为了在当前目录引入本地目录的opencomass模块。 因为有很多修改的地方。 测评VLLM 放弃吧,这个框架对VLLM的支持很差。测评方式:稳定的有且只有这一种python run.py \--datasets demo_gsm8k_c…...

随笔 | 农场、小猴子、香蕉

在一个偏西部的农场中,有着一群猴子,他们每天的任务,是将香蕉树上的香蕉摘下来,而他们的报酬是仅仅九根香蕉,每天早上四根,每天晚上五根。某一天,其中一只猴子报怨,每天早上只能吃到四根香蕉,他提议说,改成每天早上五根香蕉,其他猴子都纷纷表示同意,仅有一只小猴子…...

Day17数组的使用

package com.cc.array;public class ArrayDemo4 {public static void main(String[] args) {int [] arrays = {1,2,3,4,5};//jdk1.5之后的版本可以通果增强for寻循环来遍历数组或集合中的每一个元素//缺点在于没有下标//for(int array:arrays){// System.out.println(array);p…...

完整教程:缓存与数据库一致性的4大坑及终极解决方案

完整教程:缓存与数据库一致性的4大坑及终极解决方案pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monosp…...

Rust的Cargo用法详解 - 详解

Rust的Cargo用法详解 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important; font-si…...

串行通信接口标准(TTL、CMOS、RS232、RS422、RS485、CAN等)

TTL电平 引言 TTL是 Transistor-Transistor Logic(晶体管-晶体管逻辑)的缩写,是早期基于双极性晶体管(BJT)技术的逻辑家族。 电平特点 1. 电源电压:+5V 2. 电平标准:Voh:≥ 2.4V; Vol: ≤ 0.4V; Vih:≥ 2.0V; Vil: ≤ 0.8V;核心特点: 1. 输入悬空:TTL输入引脚如…...

攻防世界-IgniteMe - xxx

先查壳,发现没加壳,拖入ida-32反汇编了得到主函数 粗略看一下,能得到的信息有 输入的字符串长度为29,前四个字符是EIS{,最后一个字符是}想要输出Congratulations!关键的函数就是这个 4011C0函数,我们点进去看一下函数逻辑很明显,for循环之前就是把之前输入的字符串str除…...

C 语言 之 面向对象(一)

C 语言 之 面向对象(一)C 语言 之 面向对象(一) 了解C语言面向对象之前首先需要对C语言的指针、结构体有基本了解。 指针 正常使用数组: void hello(){#define count 10// shint a[count] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};for(int i = 0; i < count; i ++ ){printf(…...

for_switch

func forCase() {for i := 0; i < 10; i++ {if i == 5 {continue}fmt.Println("位置1 执行 for 语句块 i:", i)}fmt.Println("-------循环 slice ------")list := []int{1, 2, 3, 4, 5}for index, value := range list {fmt.Println("循环切片 执…...

快速幂

前题引入 我们平时用的pow函数速度太慢了怎么办,我就就需要快速幂(意思废话) 题目分析 前题铺垫 你只是需要知道一个非常简单的东西 a^b + a^c =a^(b+c) 思路 既然暴力是O(b)的,那我们是不是可以考虑O(log b) 那我们尝试将b除以2 那么就可以知道a^b = a^b/2 + a^b/2 但是我…...

模拟退火

#include<bits/stdc++.h> using namespace std; double kai=10000,eps=1,jiang=0.92,fw;//fw 记得赋值 mt19937 rd(time(0)); #define bu t*(rd()%(2*(int)fw)*1.0-fw) #define gl 1.0*rand()/RAND_MAX int ans,sx;//题目要求时开 double int cha(int x) {/**/return a…...

记录我见过的神人

魔丸《待审核》 注:团长高仿号申请进团焯神观察兵古风 古风...

DOS指令学习

打开CMD的方式 1.开始+系统+命令指示符 2.Win键+R 输入cmd 打开控制台(推荐使用) 3.在任意的文件下面,按住shift键+鼠标右键点击,在此处打开命令窗口 4.资源管理器的地址栏前面加上cmd路径 管理员方式运行:选择以管理员方式运行 常用的Dos命令 #盘符切换 #查看当前目录下的…...

【Azure环境】使用ARM Template部署Policy模板时候报错不支持filed函数: The template function field is not valid.

问题描述 Azure Policy可以帮助治理Azure上的资源, 也可以通过ARM 模板部署。只是当Policy中包含了field 函数的时候,会出现错误!"parameters": {"keyVaultName": {"value": "[field(name)]"}} 错误信息:Unable to process temp…...