Mockito基础入门
Mockito 快速入门教程
-
概述
Mockito 是一个流行的Java单元测试Mock框架,用于接口和数据模拟。 -
依赖安装
<dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>3.12.4</version><scope>test</scope>
</dependency>
// 静态导入会使代码更简洁import static org.mockito.Mockito.*;// mock creation 创建mock对象List mockedList = mock(List.class);//using mock object 使用mock对象mockedList.add("one");mockedList.clear();//verification 验证verify(mockedList).add("one");verify(mockedList).clear();
一旦mock对象被创建了,mock对象会记住所有的交互。然后你就可能选择性的验证你感兴趣的交互。
如何做一些测试桩 (Stub)?
//You can mock concrete classes, not only interfaces// 你可以mock具体的类型,不仅只是接口LinkedList mockedList = mock(LinkedList.class);//stubbing// 测试桩when(mockedList.get(0)).thenReturn("first");when(mockedList.get(1)).thenThrow(new RuntimeException());//following prints "first"// 输出“first”System.out.println(mockedList.get(0));//following throws runtime exception// 抛出异常System.out.println(mockedList.get(1));//following prints "null" because get(999) was not stubbed// 因为get(999) 没有打桩,因此输出nullSystem.out.println(mockedList.get(999));//Although it is possible to verify a stubbed invocation, usually it's just redundant//If your code cares what get(0) returns then something else breaks (often before even verify() gets executed).//If your code doesn't care what get(0) returns then it should not be stubbed. Not convinced? See here.// 验证get(0)被调用的次数verify(mockedList).get(0);
默认情况下,所有的函数都有返回值。mock函数默认返回的是null,一个空的集合或者一个被对象类型包装的内置类型,例如0、false对应的对象类型为Integer、Boolean;
测试桩函数可以被覆写 : 例如常见的测试桩函数可以用于初始化夹具,但是测试函数能够覆写它。请注意,覆写测试桩函数是一种可能存在潜在问题的做法;
一旦测试桩函数被调用,该函数将会一致返回固定的值;
上一次调用测试桩函数有时候极为重要-当你调用一个函数很多次时,最后一次调用可能是你所感兴趣的。
import static org.mockito.Mockito.*;// 创建mock对象
// 你可以mock具体的类型,不仅只是接口
List mockedList = mock(List.class);
// 对于高版本Mockito 4.10.0+,可以写的更简洁
// List mockedList = mock();// 下面添加测试桩(stubbing),指定mock的行为
// ”当“ 调用 mockedList.get(0) 返回 "first"
when(mockedList.get(0)).thenReturn("first");// 下面代码将打印 "first"
System.out.println(mockedList.get(0));// 下面将打印 "null",因为 get(999) 没有被打桩
System.out.println(mockedList.get(999));
上面示例,首先我们使用 Mockito 中的 mock 静态方法创建mock对象。或使用 @Mock 注解,通过 when()/given() 指定mock行为。例如上面当调用 mockedList.get(0) 将返回 “first”,这一过程专业术语叫做“打桩”(stubbing)。
除了 mock()/@Mock,还可使用 spy()/@Spy,两者区别是 spy 是部分mock,如果不打桩执行的是真实的方法。
使用 @InjectMocks 注解实现依赖注入,自动将mock/spy对象注入到被测试对象中。
Mockito 中的 @Mock, @Spy, @Captor 及 @InjectMocks 注解
开始之前,我们需要先使 Mockito 注解生效,有几种方法:
方法一:在JUnit 上设置 MockitoJUnitRunner
@ExtendWith(MockitoExtension.class)
public class MockitoAnnotationUnitTest {...
}
方法二:手动编码,调用 MockitoAnnotations.openMocks() 方法
@Before
public void init() {MockitoAnnotations.openMocks(this);
}
最后, 我们可以使用 MockitoJUnit.rule():
public class MockitoAnnotationsInitWithMockitoJUnitRuleUnitTest {@Rulepublic MockitoRule initRule = MockitoJUnit.rule();...
}
注意,这需要将rule 设置为 public
@Mock 注解
@Mock 是 Mockito 中用的最多的注解,我们用它来创建并注入mock对象,而不用手动调用 Mockito.mock 方法。
为了方便对比,下面这个例子中,我们先是手动mock一个ArrayList
@Test
public void whenNotUseMockAnnotation_thenCorrect() {List mockList = Mockito.mock(ArrayList.class);mockList.add("one");Mockito.verify(mockList).add("one");assertEquals(0, mockList.size());Mockito.when(mockList.size()).thenReturn(100);assertEquals(100, mockList.size());
}
然后我们通过 @Mock 注解的方式完成相同的工作:
@Mock
List<String> mockedList;@Test
public void whenUseMockAnnotation_thenMockIsInjected() {mockedList.add("one");Mockito.verify(mockedList).add("one");assertEquals(0, mockedList.size());Mockito.when(mockedList.size()).thenReturn(100);assertEquals(100, mockedList.size());
}
@Spy 注解
spy与mock的区别是,mock代理了目标对象的全部方法,spy只是部分代理
下面我们学习如何使用 @Spy 注解spy一个现有的对象实例。
我们先不用注解的方式,演示如何创建一个 spy List。
@Test
public void whenNotUseSpyAnnotation_thenCorrect() {List<String> spyList = Mockito.spy(new ArrayList<String>());spyList.add("one");spyList.add("two");Mockito.verify(spyList).add("one");Mockito.verify(spyList).add("two");assertEquals(2, spyList.size());Mockito.doReturn(100).when(spyList).size();assertEquals(100, spyList.size());
}
然后我们通过 @Spy 注解的方式完成相同的工作:
@Spy
List<String> spiedList = new ArrayList<String>();@Test
public void whenUseSpyAnnotation_thenSpyIsInjectedCorrectly() {spiedList.add("one");spiedList.add("two");Mockito.verify(spiedList).add("one");Mockito.verify(spiedList).add("two");assertEquals(2, spiedList.size());Mockito.doReturn(100).when(spiedList).size();assertEquals(100, spiedList.size());
}
本例中,我们:调用真实的 spiedList.add() 方法,向 spiedList 中新增元素使用Mockito.doReturn() 修饰后,spiedList.size() 会返回 100 而非 2
@Captor 注解
接下来让我们看看如何使用 @Captor 注解创建 ArgumentCaptor 实例。
在下面的示例中,我们先不使用 @Captor 注解,手动创建一个 ArgumentCaptor:
@Test
public void whenUseCaptorAnnotation_thenTheSame() {List mockList = Mockito.mock(List.class);ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class);mockList.add("one");Mockito.verify(mockList).add(arg.capture());assertEquals("one", arg.getValue());
}
现在,让我们使用 @Captor 注解来创建 ArgumentCaptor:
@Mock
List mockedList;@Captor
ArgumentCaptor argCaptor;@Test
public void whenUseCaptorAnnotation_thenTheSam() {mockedList.add("one");Mockito.verify(mockedList).add(argCaptor.capture());assertEquals("one", argCaptor.getValue());
}
@InjectMocks 注解
现在我们来讨论如何使用 @InjectMocks 注解将mock字段自动注入到被测试对象中。
在下面的示例中,我们将使用 @InjectMocks 把mock的 wordMap 注入到 MyDictionary dic 中:
@Mock
Map<String, String> wordMap;@InjectMocks
MyDictionary dic = new MyDictionary();@Test
public void whenUseInjectMocksAnnotation_thenCorrect() {Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning");assertEquals("aMeaning", dic.getMeaning("aWord"));
}
下面是 MyDictionary 类:
public class MyDictionary {Map<String, String> wordMap;public MyDictionary() {wordMap = new HashMap<String, String>();}public void add(final String word, final String meaning) {wordMap.put(word, meaning);}public String getMeaning(final String word) {return wordMap.get(word);}
}
将Mock注入Spy中
与前面测试类似,我们可能想在spy中注入一个mock:
@Mock
Map<String, String> wordMap;@Spy
MyDictionary spyDic = new MyDictionary();
然而,Mockito 并不支持将mock注入spy,因此下面的测试会出现异常:
@Test
public void whenUseInjectMocksAnnotation_thenCorrect() { Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning"); assertEquals("aMeaning", spyDic.getMeaning("aWord"));
}
如果我们想在 spy 中使用 mock,可以通过构造函数手动注入 mock:
MyDictionary(Map<String, String> wordMap) {this.wordMap = wordMap;
}
现在需要我们手动创建spy,而不使用注释:
@Mock
Map<String, String> wordMap; MyDictionary spyDic;@BeforeEach
public void init() {MockitoAnnotations.openMocks(this);spyDic = Mockito.spy(new MyDictionary(wordMap));
}
现在测试将通过。
使用注解时遇到空指针
通常,当我们使用 @Mock 或 @Spy 注解时,可能会遇到 NullPointerException 异常:
public class MockitoAnnotationsUninitializedUnitTest {@MockList<String> mockedList;@Test(expected = NullPointerException.class)public void whenMockitoAnnotationsUninitialized_thenNPEThrown() {Mockito.when(mockedList.size()).thenReturn(1);}
}
大多数情况下,是因为我们没有启用 Mockito 注解。所以请查看我们第一节的内容,使用Mockito前别忘了先初始化。
备注
最后,这里有一些关于 Mockito 注解的说明:
- Mockito 的注解减少了重复的mock代码
- 它们使测试更具可读性。
- @InjectMocks 是注入 @Spy 和 @Mock 实例所必需的。
- 参数匹配器 (matchers)
Mockito以自然的java风格来验证参数值: 使用equals()函数。有时,当需要额外的灵活性时你可能需要使用参数匹配器,也就是argument matchers :
//stubbing using built-in anyInt() argument matcher
// 使用内置的anyInt()参数匹配器
when(mockedList.get(anyInt())).thenReturn(“element”);
//stubbing using custom matcher (let’s say isValid() returns your own matcher implementation):
// 使用自定义的参数匹配器( 在isValid()函数中返回你自己的匹配器实现 )
when(mockedList.contains(argThat(isValid()))).thenReturn(“element”);
//following prints “element”
// 输出element
System.out.println(mockedList.get(999));
//you can also verify using an argument matcher
// 你也可以验证参数匹配器
verify(mockedList).get(anyInt());
参数匹配器使验证和测试桩变得更灵活。点击这里查看更多内置的匹配器以及自定义参数匹配器或者hamcrest 匹配器的示例。
如果仅仅是获取自定义参数匹配器的信息,查看ArgumentMatcher类文档即可。
为了合理的使用复杂的参数匹配,使用equals()与anyX() 的匹配器会使得测试代码更简洁、简单。有时,会迫使你重构代码以使用equals()匹配或者实现equals()函数来帮助你进行测试。
同时建议你阅读第15章节或者ArgumentCaptor类文档。ArgumentCaptor是一个能够捕获参数值的特俗参数匹配器。
参数匹配器的注意点 :
如果你使用参数匹配器,所有参数都必须由匹配器提供。
示例 : ( 该示例展示了如何多次应用于测试桩函数的验证 )
verify(mock).someMethod(anyInt(), anyString(), eq(“third argument”));
//above is correct - eq() is also an argument matcher
// 上述代码是正确的,因为eq()也是一个参数匹配器
verify(mock).someMethod(anyInt(), anyString(), “third argument”);
//above is incorrect - exception will be thrown because third argument
// 上述代码是错误的,因为所有参数必须由匹配器提供,而参数"third argument"并非由参数匹配器提供,因此的缘故会抛出异常
像anyObject(), eq()这样的匹配器函数不会返回匹配器。它们会在内部将匹配器记录到一个栈当中,并且返回一个假的值,通常为null。这样的实现是由于被Java编译器强加的静态类型安全。结果就是你不能在验证或者测试桩函数之外使用anyObject(), eq()函数。
- 验证函数的确切、最少、从未调用次数
//using mock
mockedList.add(“once”);
mockedList.add(“twice”);
mockedList.add(“twice”);
mockedList.add(“three times”);
mockedList.add(“three times”);
mockedList.add(“three times”);
//following two verifications work exactly the same - times(1) is used by default
// 下面的两个验证函数效果一样,因为verify默认验证的就是times(1)
verify(mockedList).add(“once”);
verify(mockedList, times(1)).add(“once”);
//exact number of invocations verification
// 验证具体的执行次数
verify(mockedList, times(2)).add(“twice”);
verify(mockedList, times(3)).add(“three times”);
//verification using never(). never() is an alias to times(0)
// 使用never()进行验证,never相当于times(0)
verify(mockedList, never()).add(“never happened”);
//verification using atLeast()/atMost()
// 使用atLeast()/atMost()
verify(mockedList, atLeastOnce()).add(“three times”);
verify(mockedList, atLeast(2)).add(“five times”);
verify(mockedList, atMost(5)).add(“three times”);
verify函数默认验证的是执行了times(1),也就是某个测试函数是否执行了1次.因此,times(1)通常被省略了。
- 为返回值为void的函数通过Stub抛出异常
doThrow(new RuntimeException()).when(mockedList).clear();
//following throws RuntimeException:
// 调用这句代码会抛出异常
mockedList.clear();
关于doThrow|doAnswer 等函数族的信息请阅读第十二章节。
最初,stubVoid(Object) 函数用于为无返回值的函数打桩。现在stubVoid()函数已经过时,doThrow(Throwable)成为了它的继承者。这是为了提升与 doAnswer(Answer) 函数族的可读性与一致性。
- 验证执行执行顺序
// A. Single mock whose methods must be invoked in a particular order
// A. 验证mock一个对象的函数执行顺序
List singleMock = mock(List.class);
//using a single mock
singleMock.add(“was added first”);
singleMock.add(“was added second”);
//create an inOrder verifier for a single mock
// 为该mock对象创建一个inOrder对象
InOrder inOrder = inOrder(singleMock);
//following will make sure that add is first called with "was added first, then with “was added second”
// 确保add函数首先执行的是add(“was added first”),然后才是add(“was added second”)
inOrder.verify(singleMock).add(“was added first”);
inOrder.verify(singleMock).add(“was added second”);
// B. Multiple mocks that must be used in a particular order
// B .验证多个mock对象的函数执行顺序
List firstMock = mock(List.class);
List secondMock = mock(List.class);
//using mocks
firstMock.add(“was called first”);
secondMock.add(“was called second”);
//create inOrder object passing any mocks that need to be verified in order
// 为这两个Mock对象创建inOrder对象
InOrder inOrder = inOrder(firstMock, secondMock);
//following will make sure that firstMock was called before secondMock
// 验证它们的执行顺序
inOrder.verify(firstMock).add(“was called first”);
inOrder.verify(secondMock).add(“was called second”);
// Oh, and A + B can be mixed together at will
验证执行顺序是非常灵活的-你不需要一个一个的验证所有交互,只需要验证你感兴趣的对象即可。 另外,你可以仅通过那些需要验证顺序的mock对象来创建InOrder对象。
- 确保交互(interaction)操作不会执行在mock对象上
//using mocks - only mockOne is interacted
// 使用Mock对象
mockOne.add(“one”);
//ordinary verification
// 普通验证
verify(mockOne).add(“one”);
//verify that method was never called on a mock
// 验证某个交互是否从未被执行
verify(mockOne, never()).add(“two”);
//verify that other mocks were not interacted
// 验证mock对象没有交互过
verifyZeroInteractions(mockTwo, mockThree);
- 查找冗余的调用
//using mocks
mockedList.add(“one”);
mockedList.add(“two”);
verify(mockedList).add(“one”);
//following verification will fail
// 下面的验证将会失败
verifyNoMoreInteractions(mockedList);
一些用户可能会在频繁地使用verifyNoMoreInteractions(),甚至在每个测试函数中都用。但是verifyNoMoreInteractions()并不建议在每个测试函数中都使用。verifyNoMoreInteractions()在交互测试套件中只是一个便利的验证,它的作用是当你需要验证是否存在冗余调用时。滥用它将导致测试代码的可维护性降低。你可以阅读这篇文档来了解更多相关信息。
never()是一种更为明显且易于理解的形式。
-
简化mock对象的创建
最小化重复的创建代码
使测试类的代码可读性更高
使验证错误更易于阅读,因为字段名可用于标识mock对象
public class ArticleManagerTest {@Mock private ArticleCalculator calculator;
@Mock private ArticleDatabase database;
@Mock private UserProvider userProvider;private ArticleManager manager;
注意!下面这句代码需要在运行测试函数之前被调用,一般放到测试类的基类或者test runner中:
MockitoAnnotations.initMocks(testClass);
你可以使用内置的runner: MockitoJUnitRunner runner 或者一个rule : MockitoRule。 关于mock注解的更多信息可以阅读MockitoAnnotations文档。
- 为连续的调用做测试桩 (stub)
有时我们需要为同一个函数调用的不同的返回值或异常做测试桩。典型的运用就是使用mock迭代器。 原始版本的Mockito并没有这个特性,例如,可以使用Iterable或者简单的集合来替换迭代器。这些方法提供了更自然的方式,在一些场景中为连续的调用做测试桩会很有用。示例如下 :
when(mock.someMethod(“some arg”))
.thenThrow(new RuntimeException())
.thenReturn(“foo”);
//First call: throws runtime exception:
// 第一次调用 : 抛出运行时异常
mock.someMethod(“some arg”);
//Second call: prints “foo”
// 第二次调用 : 输出"foo"
System.out.println(mock.someMethod(“some arg”));
//Any consecutive call: prints “foo” as well (last stubbing wins).
// 后续调用 : 也是输出"foo"
System.out.println(mock.someMethod(“some arg”));
另外,连续调用的另一种更简短的版本 :
// 第一次调用时返回"one",第二次返回"two",第三次返回"three"
when(mock.someMethod(“some arg”))
.thenReturn(“one”, “two”, “three”);
- 为回调做测试桩
Allows stubbing with generic Answer interface. 运行为泛型接口Answer打桩。
在最初的Mockito里也没有这个具有争议性的特性。我们建议使用thenReturn() 或thenThrow()来打桩。这两种方法足够用于测试或者测试驱动开发。
when(mock.someMethod(anyString())).thenAnswer(new Answer() {
Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
Object mock = invocation.getMock();
return "called with arguments: " + args;
}
});
//Following prints “called with arguments: foo”
// 输出 : “called with arguments: foo”
System.out.println(mock.someMethod(“foo”));
- doReturn()、doThrow()、doAnswer()、doNothing()、doCallRealMethod()系列方法的运用
通过when(Object)为无返回值的函数打桩有不同的方法,因为编译器不喜欢void函数在括号内…
使用doThrow(Throwable) 替换stubVoid(Object)来为void函数打桩是为了与doAnswer()等函数族保持一致性。
当你想为void函数打桩时使用含有一个exception 参数的doAnswer() :
doThrow(new RuntimeException()).when(mockedList).clear();
//following throws RuntimeException:
// 下面的代码会抛出异常
mockedList.clear();
当你调用doThrow(), doAnswer(), doNothing(), doReturn() and doCallRealMethod() 这些函数时可以在适当的位置调用when()函数. 当你需要下面这些功能时这是必须的:
测试void函数
在受监控的对象上测试函数
不知一次的测试为同一个函数,在测试过程中改变mock对象的行为。
但是在调用when()函数时你可以选择是否调用这些上述这些函数。
关于这些方法的信息:
doReturn(Object)
doThrow(Throwable)
doThrow(Class)
doAnswer(Answer)
doNothing()
doCallRealMethod()
- 监控真实对象
你可以为真实对象创建一个监控(spy)对象。当你使用这个spy对象时真实的对象也会也调用,除非它的函数被stub了。尽量少使用spy对象,使用时也需要小心形式,例如spy对象可以用来处理遗留代码。
监控一个真实的对象可以与“局部mock对象”概念结合起来。在1.8之前,mockito的监控功能并不是真正的局部mock对象。原因是我们认为局部mock对象的实现方式并不好,在某些时候我发现一些使用局部mock对象的合法用例。(第三方接口、临时重构遗留代码,完整的文章在这里 )
List list = new LinkedList();
List spy = spy(list);
//optionally, you can stub out some methods:
// 你可以为某些函数打桩
when(spy.size()).thenReturn(100);
//using the spy calls real methods
// 通过spy对象调用真实对象的函数
spy.add(“one”);
spy.add(“two”);
//prints “one” - the first element of a list
// 输出第一个元素
System.out.println(spy.get(0));
//size() method was stubbed - 100 is printed
// 因为size()函数被打桩了,因此这里返回的是100
System.out.println(spy.size());
//optionally, you can verify
// 交互验证
verify(spy).add(“one”);
verify(spy).add(“two”);
理解监控真实对象非常重要!
有时,在监控对象上使用when(Object)来进行打桩是不可能或者不切实际的。因此,当使用监控对象时请考虑doReturn|Answer|Throw()函数族来进行打桩。例如 :
List list = new LinkedList();
List spy = spy(list);
//Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
// 不可能 : 因为当调用spy.get(0)时会调用真实对象的get(0)函数,此时会发生IndexOutOfBoundsException异常,因为真实List对象是空的
when(spy.get(0)).thenReturn(“foo”);
//You have to use doReturn() for stubbing
// 你需要使用doReturn()来打桩
doReturn(“foo”).when(spy).get(0);
Mockito并不会为真实对象代理函数调用,实际上它会拷贝真实对象。因此如果你保留了真实对象并且与之交互,不要期望从监控对象得到正确的结果。当你在监控对象上调用一个没有被stub的函数时并不会调用真实对象的对应函数,你不会在真实对象上看到任何效果。
因此结论就是 : 当你在监控一个真实对象时,你想在stub这个真实对象的函数,那么就是在自找麻烦。或者你根本不应该验证这些函数。
相关文章:
Mockito基础入门
Mockito 快速入门教程 概述 Mockito 是一个流行的Java单元测试Mock框架,用于接口和数据模拟。 依赖安装 <dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>3.12.4</version>…...
o(∩_∩)o设置代理访问博客(五)o(∩_∩)o --使用BeeWare打包应用
背景: 最近了解了一个新的打包框架BeeWare,据说他支持的平台要比nuitka更多。利用之前访问博客的脚本,来尝试一下打包windows应用程序。 BeeWare 从零开始 环境:python -m pip install briefcase 使用pycharm新建了一个名为bee…...
使用GPT-4o mini融合GraphRAG技术进行实战应用
什么是gpt-4o mini OpenAI 推出 GPT-4o mini,这是他们最具成本效益的小型模型。它的定价为每百万输入代币 15 美分,每百万输出代币 60 美分,比之前的 Frontier 型号便宜一个数量级,比 GPT-3.5 Turbo 便宜 60% 以上。目前…...
嵌入式人工智能(45-基于树莓派4B的扩展板-舵机驱动板PCA9685)
1、简介 智能小车、机械臂、摄像头云台会有多个舵机,而微控制器芯片的PWM输出引脚不够的情况下,就可以用PCA9685(16路舵机)来解决这一问题。 PCA9685是一款I2C总线控制的16通道LED控制器,专为红/绿/蓝/琥珀ÿ…...
3.11.样式迁移
样式迁移 使用卷积神经网络,自动的将一个图像中的风格应用在另一图像之上,即样式迁移(style transfer) 为了完成这一过程,我们需要两张输入图像:一张是内容图像,一张是风格图像,随后使用神经网络修…...
Spring统一返回类型中关于String的问题
文章目录 1. 问题铺垫2. 解决方法3. 问题分析4 解决方法解释 1. 问题铺垫 首先设置了以下代码统一处理返回类型 ControllerAdvice public class ResponseAdvice implements ResponseBodyAdvice {Overridepublic boolean supports(MethodParameter returnType, Class converte…...
Python酷库之旅-第三方库Pandas(065)
目录 一、用法精讲 256、pandas.Series.sparse方法 256-1、语法 256-2、参数 256-3、功能 256-4、返回值 256-5、说明 256-6、用法 256-6-1、数据准备 256-6-2、代码示例 256-6-3、结果输出 257、pandas.DataFrame.sparse方法 257-1、语法 257-2、参数 257-3、功…...
Go语言标准库中的双向链表的基本用法
什么是二分查找区间? 什么是链表? 链表节点的代码实现: 链表的遍历: 链表如何插入元素? go语言标准库的链表: 练习代码: package mainimport ("container/list""fm…...
Cocos Creator2D游戏开发(9)-飞机大战(7)-爆炸效果
这个爆炸效果我卡在这里好长时间,视频反复的看, 然后把代码反复的测试,修改,终于给弄出来 视频中这段,作者也是修改了好几次, 跟着做也走了不少弯路; 最后反正弄出来了; 有几个坑; ① 动画体创建位置是enemy_prefab ② enemy_prefab预制体下不用放动画就行; ③ 代码中引用Anima…...
Redis入门概述
Redis 概述 Redis 是速度非常快的非关系型(NoSQL)基于内存的键值数据库。 键的类型只能为字符串,值可以支持五种数据类型:字符串(stirng)、列表(list)、集合(set)、散列表(hash)、有…...
Cursor搭配cmake实现C++程序的编译、运行和调试
Cursor搭配cmake实现C程序的编译、运行和调试 Cursor是一个开源的AI编程编辑器,开源地址https://github.com/getcursor/cursor ,它其实是一个集成了Chat-GPT的VS Code。 关于VS Code和VS的对比可以参考这篇文章VS Code 和 Visual Studio 哪个更好&…...
读零信任网络:在不可信网络中构建安全系统09用户信任
1. 用户信任 1.1. 将设备身份和用户身份混为一谈会导致一些显而易见的问题 1.1.1. 特别是当用户拥有多台设备时,而这种情况很普遍 1.1.2. 应该针对不同类型的设备提供相匹配的凭证 1.1.3. 在存在共用终端设备的情况下,所有的这些问题将更加凸显 1.2…...
二叉树的实现 c语言
注:层序所需的队列文件请参考 C语言 实现栈(顺序表)和队列(链表)-CSDN博客 一、 BTree.h 函数包含: // 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树 // 二叉树销毁 // 二叉树节点个数 // 二叉树叶子节点个数 // 二叉树第k层节点个数 /…...
【Git】如何优雅地使用Git中的tag来管理项目版本
目录 tagtag 和 branch区别操作命令打tag,当前分支标记tag提交到远程服务器删除本地tag删除远程tag切换到特定的tag查看所有tag查看标签详细信息 好书推荐 tag Git中的tag(标签)用于给项目在特定时间点(某个版本发布)…...
白骑士的PyCharm教学进阶篇 2.3 测试与自动化
系列目录 上一篇:白骑士的PyCharm教学进阶篇 2.2 高级调试技术 在现代软件开发中,测试和自动化是保证代码质量和项目稳定性的重要环节。PyCharm作为一款强大的Python IDE,提供了丰富的工具来支持单元测试和自动化测试。本篇将详细介绍单元测…...
spark写入redis报错空指针
aused by: java.lang.NullPointerException 1、spark集群 每个executor是否 与redis集群 网络策略是否通 2、写入redis的数据是否 有 null值 需要把null值 转成空字符串 3、可以用 pipleline 并行写数据...
【Material-UI】Autocomplete中的禁用选项:Disabled options
文章目录 一、简介二、基本用法三、进阶用法1. 动态禁用2. 提示禁用原因3. 复杂的禁用条件 四、最佳实践1. 一致性2. 提供反馈3. 优化性能 五、总结 Material-UI的Autocomplete组件提供了丰富的功能,包括禁用特定选项的能力。这一特性对于限制用户选择、提供更好的用…...
力扣287【寻找重复数】
给定一个包含 n 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。 假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。 你设计的解决方案必须 不修改 数组 nums 且只用常…...
学习笔记一
vector 在创建时指定初始大小和初始值: vector<int> a(5, 1) // 包含 5 个整数的 vector,每个值都为 1 可以使用 push_back 方法向 vector 中添加元素: a.push_back(7) // 将整数 7 添加到 vector 的末尾 可以使用 size(…...
星纪魅族双轮驱动遇阻:AI手机与造车梦能否照进现实?
在科技行业风起云涌的浪潮中,星纪魅族近期的一系列动作引起了广泛关注。从高层换血到全面押注AI,再到宣布造车计划,每一步都显得雄心勃勃,但深入剖析后不难发现,其未来发展之路实则布满荆棘。 星纪魅族选择“All in AI…...
深入理解 C 语言中的联合体
目录 引言 一、 联合体的定义与基本用法 1.联合体的定义 2.基本用法 二、 联合体与结构体的区别 1.结构体 2.联合体 3.对比 三、联合体的优势 1. 节省内存 2. 提高效率 3. 代码简洁性 四、联合体的存储细节 1.内存对齐 2.大小计算 五、联合体的高级用法 1.匿…...
Unity3D 物体圆周运动
Unity3D 实现一个 2D 物体沿着圆周进行运动。 物体圆周运动 前段时间在开发一个小游戏时,需要实现火箭沿着一个圆形轨道进行圆周运动。 以前面试的时候也被问到过这类问题(如何让一个 2D 物体做圆周运动),所以还是记录一下实现…...
无人机无人车固态锂电池技术详解
随着无人机和无人车技术的飞速发展,对高性能、高安全性电池的需求日益迫切。固态锂电池作为下一代电池技术的代表,正逐步从实验室走向市场,为无人机和无人车等应用领域带来革命性的变化。相比传统液态锂电池,固态锂电池在能量密度…...
策略模式的一次应用
项目的需求是将一组图像按照相似度分类。 采用了模板匹配计算相似度的实现方式。 #include <opencv2/core.hpp> #include <openev2/core/utility.hpp> #include <opencv2/highqui.hpp> #include <openav2/imgproc.hpp> cv::Mat image matched; double …...
终极指南:3D 数据科学系统和工具
该蓝图分享了 AI 方法、算法、工具、模板和 6 步系统,用于为 3D 模型构建数据科学解决方案:3D 数据采集、分析、建模、可视化和部署。 3D 数据科学系统的核心组件和交叉学科 欢迎来到雲闪世界。建立 3D 数据科学项目涉及结合数据工程、数据分析和可视…...
Docker Container(容器)
一、概念 容器是镜像的运行实体。镜像是静态的只读文件,而容器带有运行时需要的可写文件层,并且容器中的进程属于运行状态。即容器运行着真正的应用进程。容器有初建、运行、停止、暂停和删除五种状态。 虽然容器的本质是主机上运行的一个进程…...
确保Apache Flink流处理的数据一致性和可靠性
Apache Flink是一个用于大规模数据流处理的开源框架,它提供了多种机制来保证在分布式环境中数据的一致性和可靠性。在实时流处理中,数据的一致性和可靠性是至关重要的,因为它们直接影响到数据处理结果的准确性和系统的稳定性。本文将详细介绍…...
MUSE Multi-View Contrastive Learningfor Heterophilic Graphs
发表于:CIKM 推荐指数: #paper/⭐ 一句话总结:融合了GCN(A,X)和GCN(A,I),创新性不足,因此只能B会 流程: 融合部分: h i f h i s λ i h i c h_i^fh_i^s\lambda_ih_i^c hifhisλihic 由于有n个 λ \lambda λ.因此作者加了如下优化: L ϕ ∑ i 1 N λ i s ( h i …...
搭建pxe网络安装环境实现服务器自动部署
一:主机查看本地镜像挂载的位置 [rootnginx ~]# df [rootnginx ~]# cd /rhe17 [rootnginx rhe17]# ls [rootnginx rhe17]# cd isolinux/ [rootnginx isolinux]# ls文件内容指定了触法镜像,内核,显示界面等 二:通过网络将以上文件…...
在VScode中导入conda环境的记录【原创】
今天在vscode编辑器中运行一个python代码,发现终端可以运行,但是编辑器中点击Run会显示缺包,但是python包明明是有的,在自己的conda环境中。后来发现,是vscode没有发现我自己创建的conda环境,在vscode中导入…...
机器学习 第10章-降维与度量学习
机器学习 第10章-降维与度量学习 10.1 k近邻学习 k近邻(k-Nearest Neighbor,简称kNN)学习是一种常用的监督学习方法其工作机制非常简单:给定测试样本,基于某种距离度量找出训练集中与其最靠近的k个训练样本,然后基于这k个“邻居”的信息来进行预测。通…...
手机三要素接口怎么对接呢?(二)
一、什么是手机三要素? 手机三要素又叫运营商三要素,运营商实名认证,运营商实名核验,手机三要素实名验证,手机三要素实名核验,每个人的称呼都不同,但是入参和出参是一样的。 输入姓名、身份证…...
【Devops】CertD 完全免费、自动申请、自动部署SSL证书一站式管理工具 | 自动化HTTPS | 3个月SSL自动轮换
CertD CertD 是一个免费全自动申请和自动部署更新SSL证书的工具。 后缀D取自linux守护进程的命名风格,意为证书守护进程。 关键字:证书自动申请、证书自动更新、证书自动续期、证书自动续签 一、特性 本项目不仅支持证书申请过程自动化,还…...
高级Vue.js面试指南:关键概念、最佳实践和性能优化策略
目录 1. 什么是 Vue.js? 2. Vue的生命周期钩子是什么? 3. 计算属性和侦听器有什么区别? 4. 什么是虚拟DOM? 5. 解释 Vue 的响应式系统原理。 6. Vue中的v-if和v-show有什么区别? 7. 如何在 Vue 中使用路由? 8. 描述组件间的通信方式。 9. 什么是单文件组件? 10…...
MySQL基础练习题21-按日期分组销售产品
目录 题目 准备数据 分析数据 总结 题目 找出每个日期、销售的不同产品的数量及其名称。每个日期的销售产品名称应按词典序排列。 返回按 sell_date 排序的结果表。 准备数据 ## 创建库 create database db; use db;## 创建表 Create table If Not Exists Activities (s…...
oracle(19c)用户管理
简介 本文介绍 Oracle 中的用户管理,包含以下内容: 概念介绍 系统用户 解锁 hr 用户 创建用户 用户相关案例 使用 Profile 管理用户口令 Oracle 的认证方式 重置管理员(sys)密码 1. 概念介绍 使用前可以自行安装oracle数据库 oracle19c安装&a…...
数学建模--禁忌搜索
目录 算法基本原理 关键要素 应用实例 实现细节 python代码示例 总结 禁忌搜索算法在解决哪些具体类型的组合优化问题中最有效? 禁忌搜索算法的邻域结构设计有哪些最佳实践或案例研究? 如何动态更新禁忌表以提高禁忌搜索算法的效率和性能&#…...
RabbitMQ动态创建生产者动态创建消费者
生产者 controller package org.log.rabbitmqdemo1.demos.web.producer;import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;RestController public class Produc…...
HTML-07.表格标签
一、要制作的表格如下 二、代码如下 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>表格标签<…...
[Git][分支管理][下]详细讲解
目录 1.合并冲突2.分支管理策略3.分支策略1.基本原则2.bug分支3.删除临时分支 1.合并冲突 在实际分⽀合并的时候,有时候可能会遇到代码冲突的问题,例如: dev分支在写一部分代码,而master分支也没闲着,也在写着同一份代…...
Python面试宝典第27题:全排列
题目 给定一个不含重复数字的数组nums,返回其所有可能的全排列 。备注:可以按任意顺序返回答案。 示例 1: 输入:nums [1,2,3] 输出:[[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]] 示例 2: 输…...
C语言调试宏全面总结(六大板块)
C语言调试宏进阶篇:实用指南与案例解析C语言调试宏高级技巧与最佳实践C语言调试宏的深度探索与性能考量C语言调试宏在嵌入式系统中的应用与挑战C语言调试宏在多线程环境中的应用与策略C语言调试宏在并发编程中的高级应用 C语言调试宏进阶篇:实用指南与案…...
C++学习之路(1)— 第一个HelloWorld程序
C学习之路(1)— 第一个HelloWorld程序 一、前言 C在C语言的基础上添加了对面向对象编程和泛型编程的支持,在 20世纪90年代便是最重要的编程语言之一,并在21世纪仍保持强劲势头。C继承了C语言高效、简洁、快速和可移植性的传统。 …...
Ionic 滑动框:实现高效移动应用交互体验
Ionic 滑动框:实现高效移动应用交互体验 Ionic 是一个强大的开源框架,用于构建高性能、高质量的移动端和网页应用。它以其优雅的UI组件和强大的功能而闻名,其中滑动框(Slider)是Ionic UI组件库中的一个重要组成部分。滑动框提供了一种直观、互动的方式来展示图片、文本或…...
书生大模型实战营第三期——入门岛
第一关:Linux基础知识 任务如下: 任务描述闯关任务完成SSH连接与端口映射并运行hello_world.py可选任务 1将Linux基础命令在开发机上完成一遍可选任务 2使用 VSCODE 远程连接开发机并创建一个conda环境可选任务 3创建并运行test.sh文件 1. 使用密码进行…...
Android入门之路 - WebView加载数据的几种方式
之前客户端加载H5时遇到了一些问题,我为了方便解决问题,所以将对应场景复刻到了Demo中,从之前的网络加载模拟为了本地加载Html的方式,但是没想到无意被一个基础知识点卡了一些时间,翻看往昔笔记发现未曾记录这种基础场…...
git的rebase 和 merge 的区别
rebase 和 merge 的区别 Merge(合并)和 Rebase(变基)是 Git 中两种常用的分支整合方式,它们有不同的工作原理和适用场景: Merge(合并): ● Merge 操作将两个分支的不同提…...
科普文:JUC系列之Java中7种阻塞队列BlockingQueue的双锁源码解读
概叙 Queue接口与List、Set同一级别,都是继承了Collection接口**。队列是一种数据结构.它有两个基本操作:在队列尾部加人一个元素,和从队列头部移除一个元素,队列以一种先进先出的方式管理数据。 队列分为两种&#x…...
Kafka生产者(二)
1、生产者消息发送流程 1.1 发送原理 在消息发送的过程中,涉及到了两个线程——main 线程和 Sender 线程。在 main 线程中创建了一个双端队列 RecordAccumulator。main 线程将消息发送给 RecordAccumulator,Sender 线程不断从 RecordAccumulator 中拉取…...
ReentrantLock源码分析
文章目录 一、AQS1、state属性2、等待队列3、条件变量 二、ReentrantLock1、非公平锁实现原理1.1 获取锁1.2 释放锁1.3 可重入原理1.4 可打断原理不可打断可打断 1.5 公平锁实现原理1.6 条件变量原理awaitsignal 一、AQS AQS全称是 AbstractQueuedSynchronizer,是阻…...
67国425名留学生在沪比拼商务智慧 国际视野下的商业决策较量
第二届来华留学生国际商务决策模拟大赛暨尖烽时刻国际赛总决赛于11月13日在上海财经大学落幕,江南大学的“江南香料商队”荣获全国总冠军。此次比赛由上海教育国际交流协会主办,上海财经大学国际文化交流学院与全球模拟赛事平台尖烽时刻组委会承办。来自全国32所院校的117支团…...
男子年轻时被老板批评气得失眠32年 心病难愈引发热议
11月14日,郑州一位70岁的王大爷因年轻时被老板批评而失眠32年,此事引发了广泛关注。王大爷表示,每次睡前都会想起那次不公正的批评,导致他长期无法入睡,只能依赖药物。多年来,王大爷辗转多家医院求医,但失眠问题一直未能解决,还出现了帕金森的症状。医生建议他在接受治…...
美官员炒作中国黑客活动 中方驳斥 无端指责遭反击
11月14日,外交部发言人林剑主持例行记者会。彭博社记者提问,美国官员周三发表声明称,中国政府支持的黑客进行了广泛而重大的网络间谍活动。据《纽约时报》报道,这些黑客活动的目标包括美国当选总统特朗普、当选副总统万斯以及美国副总统哈里斯。林剑对此作出回应。责任编辑…...
官方通报女童被开水烫伤:获赔32万,肇事者未满14岁不予行政处罚
11月14日,广东乳源瑶族自治县人民政府新闻办公室通报了一起校园伤害事件。9月29日,乳源瑶族自治县乳城镇中心小学一名六年级男孩李某向一年级女孩丘某泼洒热水,导致丘某二度烫伤,引起社会广泛关注。事件发生后,韶关市和乳源县领导及相关单位迅速行动,组织力量开展救治和调…...
字母主动放弃第二个60 创纪录赢人品风评:冷静谦逊获赞誉
字母主动放弃第二个60 创纪录赢人品风评11月14日,密尔沃基雄鹿在主场迎战底特律活塞,最终通过加时赛以127-120获胜,取得本赛季第四场胜利及连胜。字母哥在这场比赛中表现抢眼,在44分钟内砍下59分、14个篮板、7次助攻、2次抢断和3次盖帽,展现了统治级实力。尽管他有机会冲…...
雷军现身珠海航展 登上运油20参观 小米或将入局低空经济
11月14日,第15届中国航展在珠海进行到第三天。小米公司创始人、董事长雷军出现在航展现场,认真听取了讲解员对参展装备的介绍。此前,在4月25日的北京车展上,雷军曾亲自到访小鹏汇天飞行汽车展台,为即将量产的飞行汽车站台带货,引发广泛关注。如今,小米SU 7在市场上取得了…...