Mockito5.2.0学习
Mockito是mocking框架,它让你用简洁的API做测试。而且Mockito简单易学,它可读性强和验证语法简洁。
Mockito 是一个针对 Java 的单元测试模拟框架,它与 EasyMock 和 jMock 很相似,都是为了简化单元测试过程中测试上下文 ( 或者称之为测试驱动函数以及桩函数 ) 的搭建而开发的工具
相对于 EasyMock 和 jMock,Mockito 的优点是通过在执行后校验哪些函数已经被调用,消除了对期望行为(expectations)的需要。其它的 mocking 库需要在执行前记录期望行为(expectations),而这导致了丑陋的初始化代码。
以下学习内容基于Mockito官方5.2.0文档和中文文档2.23.4
目录
1. 验证某些行为
2. 打桩测试
3. 参数匹配器 (matchers)
内置常用匹配器在org.mockito.ArgumentMatchers里
附加匹配器在org.mockito.AdditionalMatchers里
4. 验证函数的调用次数
5. 通过打桩为无返回值函数抛出异常
6. 验证执行顺序
7. 确认交互(interaction)操作没有在mock对象上执行
8. 查找多余的调用
9. 简化mock对象的创建 - @Mock注解
10. 为连续的调用打桩,多次调用返回不同的值
11. 通过回调方式来打桩
12. doReturn()|doThrow()| doAnswer()|doNothing()|doCallRealMethod()系列方法
13. 监控真实对象
14. 修改没有测试桩的调用的默认返回值 ( 1.7版本之后 )
15. 为下一步的断言捕获参数 (1.8版本之后)
16. 真实的局部模拟对象(mocks) (1.8版本之后)
17. 重置mocks对象 (1.8版本之后)
18. 故障排查与验证框架的使用 (1.8版本之后)
19.行为驱动开发的别名 (1.8版本之后)
20. 可序列化的mocks(1.8.1版本之后)
21. 新的注解 : @Captor,@Spy,@InjectMocks (1.8.3版本之后)
22. 带超时的验证 (1.8.5版本之后)
23. 自动实例化被@Spy, @InjectMocks注解的字段以及构造函数注入 (1.9.0版本之后)
24. 单行测试桩 (1.9.0版本之后)
25. 验证被忽略的测试桩 (1.9.0版本之后)
26. mock详情 (2.2.x中改进)
27. 真实实例的委托调用 (Since 1.9.5)
28. MockMaker API (Since 1.9.5)
29. BDD 风格的验证 (Since 1.10.0)
30. 监视 或 模拟 抽象类 (1.10.12版本加入,在2.7.13 和 2.7.14版特征得到增强))
31. Mockito的模拟对象 可以通过 classloaders 序列化/反序列化 (Since 1.10.0)
32. Deep stubs 更好的泛型支持 (Since 1.10.0)
33. Mockito JUnit rule (Since 1.10.17)
34. 开启和关闭插件的开关 (Since 1.10.15)
35. 自定义验证失败信息 (Since 2.1.0)
36. Java 8 Lambda匹配器的支持 (Since 2.1.0)
37. Java 8 自定义Answer的支持 (Since 2.1.0)
38. 元数据和泛型信息保留 (Since 2.1.0)
39. 模拟final类型,枚举 和 final方法 (Since 2.1.0)
40. “严格的”Mocktio能提高生产效率并使测试用例更清晰(2.+版本之后)
41. 框架集成的高级公开API (2.10.+版本之后)
42. 用于集成的新API: 监听验证开始(verification start)事件(2.11.+版本之后)
43. 用于集成的新API: 测试框架支持MockitoSession(2.15.+版本之后)
44. org.mockito.plugins.InstantiatorProvider泄露内部API所以被org.mockito.plugins.InstantiatorProvider2替代
45. 新的JUnit Jupiter (JUnit5+) extension
46. 新的Mockito.lenient()和MockSettings.lenient()方法(2.20.0版本之后)
47. 用于在内联mocking中清除mock状态的新API(2.25.0版本之后)
48. 模拟静态方法(3.4.0版本之后)
49. 模拟对象构造(3.5.0版本之后)
50. 仅模拟接口时避免代码生成(3.12.2版本之后)
51. 把类标记为不可模拟(4.1.0版本之后)
52. @Mock注解和MockSettings.strictness()方法的新strictness属性(4.6.0版本之后)
53. 指定单个mock的mock maker(4.8.0版本之后)
54. mock/spy不指定类(4.9.0版本之后)
1. 验证某些行为
一旦mock对象被创建了,mock对象会记住所有的交互动作。然后你就可以选择性的验证你感兴趣的交互动作。
// 静态导入会使代码更简洁import static org.mockito.Mockito.*;// 创建mock对象List mockedList = mock(List.class);// 使用mock对象mockedList.add("one");mockedList.clear();// 验证verify(mockedList).add("one");verify(mockedList).clear();
2. 打桩测试
默认情况下,所有的函数都有返回值。
mock函数会适当的返回null/原始类型/原始类型的包装类/一个空的集合,例如int/Integer返回0、boolean/Boolean返回false。
打桩动作可以被覆写 : 例如常见的打桩动作可以用于公共的配置,然后在测试函数中能够重新打桩。
请注意,覆写测试桩函数可能意味着打桩动作太多了。
一旦测试桩函数被调用,该函数将会一直返回固定的值。
多次使用同一参数对同一函数打桩时,调用时返回最后一次打桩的值。
// 你可以mock具体的类,不仅只是接口LinkedList mockedList = mock(LinkedList.class);// 打桩when(mockedList.get(0)).thenReturn("first");when(mockedList.get(1)).thenThrow(new RuntimeException());// 输出“first”System.out.println(mockedList.get(0));// 抛出异常System.out.println(mockedList.get(1));// 因为get(999) 没有打桩,因此输出nullSystem.out.println(mockedList.get(999));// 虽然能验证被打桩对象的调用情况,但这通常写起来很啰嗦// 如果你关注get(0)的返回值,那么其他地方就会中断(通常在verify()执行之前)// 如果你不关注get(0)的返回值,那么它就不应该被打桩。verify(mockedList).get(0);
3. 参数匹配器 (matchers)
Mockito以自然的java风格来验证参数值: 使用equals()函数。
有时,当需要额外的灵活性时你可能需要使用参数匹配器,也就是argument matchers :
// 使用内置的anyInt()参数匹配器来打桩when(mockedList.get(anyInt())).thenReturn("element");// 使用自定义的参数匹配器来打桩( 在isValid()函数中返回你自己的匹配器实现 )when(mockedList.contains(argThat(isValid()))).thenReturn("element");// 输出“element”System.out.println(mockedList.get(999));// 你也可以用参数匹配器来验证verify(mockedList).get(anyInt());// 参数匹配器也能用Java 8 Lambda风格编写verify(mockedList).add(argThat(someString -> someString.length() > 5));
由于Mockito any(Class)和anyInt家族匹配器执行类型检查,因此它们不会匹配null参数。请改用isNull匹配器。
如果你使用了参数匹配器, 那么所有参数都要用匹配器。
verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));// 上述代码是正确的,因为eq()也是一个参数匹配器verify(mock).someMethod(anyInt(), anyString(), "third argument");// 上述代码是错误的,因为所有参数必须由匹配器提供,而参数"third argument"并非由参数匹配器提供,因此会抛出异常
内置常用匹配器在org.mockito.ArgumentMatchers里
匹配所有东西,包括null和可变参数
any()
匹配给定类型的对象,不包括null
any(Class<T> type)
匹配基本数据类型或者非null 的基本数据类型对应的对象
anyBoolean(),anyByte(),anyChar(),anyDouble(),anyFloat(),anyInt(),anyLong(),anyShort()
匹配非null的常用类的对象
anyCollection(),anyIterable(),anyList(),anyMap(),anySet(),anyString()
使用用户自定义参数匹配器
argThat(ArgumentMatcher<T> matcher)
booleanThat(ArgumentMatcher<Boolean> matcher)
byteThat(ArgumentMatcher<Byte> matcher)
charThat(ArgumentMatcher<Character> matcher)
doubleThat(ArgumentMatcher<Double> matcher)
floatThat(ArgumentMatcher<Float> matcher)
intThat(ArgumentMatcher<Integer> matcher)
longThat(ArgumentMatcher<Long> matcher)
shortThat(ArgumentMatcher<Short> matcher)
匹配相等
eq(boolean value),eq(byte value),eq(char value),eq(double value),eq(float value),eq(int value),eq(long value),eq(short value),eq(T value)
匹配字符串
contains(String substring),endsWith(String suffix),matches(String regex),matches(Pattern pattern),startsWith(String prefix)
NULL判断
isNull(),isNull(Class<T> type),isNotNull(),isNotNull(Class<T> type),notNull(),notNull(Class<T> type)
实现给定类的对象参数
isA(Class<T> type)
匹配null或者给定的类型
nullable(Class<T> clazz)
反射后与给定的对象参数一致,支持从类中排除给定字段
refEq(T value, String... excludeFields)
匹配相同引用
same(T value)
附加匹配器在org.mockito.AdditionalMatchers里
用于组合常用匹配器
//anything but not "ejb"mock.someMethod(not(eq("ejb")));//not "ejb" and not "michael jackson"mock.someMethod(and(not(eq("ejb")), not(eq("michael jackson"))));//1 or 10mock.someMethod(or(eq(1), eq(10)));
两个匹配器同时成立,T可以是对象类型或基本数据类型
and(T first, T second)
两个匹配器任一成立,T可以是对象类型或基本数据类型
or(T first, T second)
匹配器不成立,T可以是对象类型或基本数据类型
not(T first)
匹配器返回值比较大小,T可以是对象类型或基本数据类型
geq(T value),gt(T value),leq(T value),lt(T value)
匹配器返回的数组值与给定值相等,T可以是对象类型或基本数据类型
aryEq(T[] value)
使用绝对值差小于某个值判定浮点数相等
eq(double value, double delta)
eq(float value, float delta)
使用compareTo比较相等
cmpEq(T value)
匹配器返回值包含指定字符串
find(String regex)
4. 验证函数的调用次数
调用次数是使用匹配参数列表的函数调用的次数,参数列表不匹配时,不计次数
// 下面的两个验证函数效果一样,因为verify默认验证的就是times(1)verify(mockedList).add("once");verify(mockedList, times(1)).add("once");// 验证具体的执行次数verify(mockedList, times(2)).add("twice");verify(mockedList, times(3)).add("three times");// 使用never()进行验证,never相当于times(0)verify(mockedList, never()).add("never happened");// 使用atLeast()/atMost()进行验证verify(mockedList, atLeastOnce()).add("three times");verify(mockedList, atLeast(2)).add("five times");verify(mockedList, atMost(5)).add("three times");
5. 通过打桩为无返回值函数抛出异常
doThrow(new RuntimeException()).when(mockedList).clear();// 调用这句代码会抛出运行时异常mockedList.clear();
6. 验证执行顺序
// 为mock对象创建一个inOrder验证器InOrder inOrder = inOrder(singleMock);// 确保add函数首先执行的是add("was added first"),然后才是add("was added second")inOrder.verify(singleMock).add("was added first");inOrder.verify(singleMock).add("was added second");// 创建inOrder对象,将需要验证的mock对象传进来InOrder inOrder = inOrder(firstMock, secondMock);// 下面将会确认第一个模拟对象会在第二个模拟对象前被调用inOrder.verify(firstMock).add("was called first");inOrder.verify(secondMock).add("was called second");
7. 确认交互(interaction)操作没有在mock对象上执行
verifyNoInteractions(mockTwo, mockThree);
8. 查找多余的调用
// 调用mock对象mockedList.add("one");mockedList.add("two");// 验证执行了add("one")verify(mockedList).add("one");// 因为执行了未验证的add("two"),所以这里会报错verifyNoMoreInteractions(mockedList);
不建议在每个测试函数中都使用verifyNoMoreInteractions()。
在交互测试套件中只是一个便利的验证,它的作用是当你需要验证是否存在冗余调用时,滥用它将导致测试代码的可维护性降低。
9. 简化mock对象的创建 - @Mock注解
最小化重复的创建代码
使测试类的代码可读性更高
使验证错误更易于阅读,因为字段名可用于标识mock对象
public class ArticleManagerTest {@Mock private ArticleCalculator calculator;@Mock private ArticleDatabase database;@Mock private UserProvider userProvider;private ArticleManager manager;@org.junit.jupiter.api.Testvoid testSomethingInJunit5(@Mock ArticleDatabase database) {
注意! 下面这句代码需要在运行测试函数之前被调用,一般放到测试类的基类或者test runner中:
MockitoAnnotations.initMocks(testClass);
10. 为连续的调用打桩,多次调用返回不同的值
有时我们需要为同一个函数调用的不同的返回值/异常做测试桩。典型的运用案例就是对迭代器的模拟。
when(mock.someMethod("some arg")).thenThrow(new RuntimeException()).thenReturn("foo");// 第一次调用 : 抛出运行时异常mock.someMethod("some arg");// 第二次调用 : 输出"foo"System.out.println(mock.someMethod("some arg"));// 后续调用 : 也是输出"foo"System.out.println(mock.someMethod("some arg"));
另外,连续调用的另一种更简短的版本 :
// 第一次调用时返回"one",第二次返回"two",第三次返回"three"when(mock.someMethod("some arg")).thenReturn("one", "two", "three");
11. 通过回调方式来打桩
允许通过泛型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;}});// 输出 : "called with arguments: foo"System.out.println(mock.someMethod("foo"));
12. doReturn()|doThrow()| doAnswer()|doNothing()|doCallRealMethod()系列方法
通过when(Object)为无返回值的函数打桩有不同的方法,因为编译器不喜欢void函数在括号内...
当你想为void函数打桩来跑出一个异常时,使用doThrow()方法:
doThrow(new RuntimeException()).when(mockedList).clear();// 下面的代码会抛出运行时异常mockedList.clear();
当你调用doThrow(), doAnswer(), doNothing(), doReturn() and doCallRealMethod() 这些函数时,可以在适当的位置调用when()函数. 当你需要下面这些功能时这是必须的:
测试void函数
在受监控的真实对象上测试函数
同一个函数多次打桩,为了在测试过程中改变mock对象的行为。
13. 监控真实对象
你可以为真实对象创建多个监控(spy)对象。
当使用这个spy对象时,真实的方法会被调用(除非它的函数被stub了)。
尽量少使用spy对象,使用时也需要小心形式,例如spy对象可以用来处理遗留代码。
监控一个真实的对象可以与“局部mock对象”概念结合起来。
在1.8之前,mockito的监控功能并不是真正的局部mock对象。原因是我们认为局部mock对象的实现方式并不好。
在某些时候,我们发现了使用局部mock对象的合法用例。(第三方接口、临时重构遗留代码)
List list = new LinkedList();List spy = spy(list);//可选的,你可以为某些函数打桩:when(spy.size()).thenReturn(100);//通过spy对象调用真实对象的函数spy.add("one");spy.add("two");//打印第一个元素"one"System.out.println(spy.get(0));//size()函数被打桩了,因此这里打印的是100System.out.println(spy.size());//可选的,你可以做校验verify(spy).add("one");verify(spy).add("two");
有时,在监控对象上使用when(Object)来进行打桩是不可能或者不切实际的。因此,当使用监控对象时请考虑doReturn|Answer|Throw()函数族来进行打桩。
List list = new LinkedList();List spy = spy(list);//不可能 : 当调用spy.get(0)时会调用真实对象的get(0)函数,此时会发生IndexOutOfBoundsException异常(因为真实list对象还是空的)when(spy.get(0)).thenReturn("foo");//你必须使用doReturn()来打桩doReturn("foo").when(spy).get(0);
Mockito并不会代理传递来的真实对象,实际上它会拷贝一份真实对象。
因此如果你保留了真实对象并且与之交互,不要期望从监控对象得到正确的结果。
当你在监控对象上调用一个没有被stub的函数时,并不会调用真实对象的对应函数,你不会在真实对象上看到任何效果。
注意final方法,Mockito不能mock final方法,所以底线是:当你监视真实对象,你尝试在fianl方法上打桩,那么会有问题。同样,你也不能验证这些方法。
14. 修改没有测试桩的调用的默认返回值 ( 1.7版本之后 )
你可以创建mock对象,用指定策略来作为它的返回值。这是一个高级特性,通常来说,你不需要写这样的测试。
无论如何,它对于遗留系统来说是很有用处的。
当你没有为函数调用打桩时,你可以指定一个默认的answer。
Foo mock = mock(Foo.class, Mockito.RETURNS_SMART_NULLS);Foo mockTwo = mock(Foo.class, new YourOwnAnswer());
RETURNS_DEFAULTS
首先尝试全局配置,如果没有全局配置,那么它将使用返回零、空集合、null等的默认答案。
RETURNS_SMART_NULLS
首先尝试返回普通值(零、空集合、空字符串等),然后尝试返回SmartNull。如果返回类型是final,则返回纯null。
RETURNS_MOCKS
首先尝试返回普通值(零、空集合、空字符串等),然后尝试返回mock对象。如果返回类型不能被模拟(例如是final),则返回纯null。
RETURNS_DEEP_STUBS
// 这样Foo mock = mock(Foo.class, RETURNS_DEEP_STUBS);when(mock.getBar().getName(), "deep");// 等价于下面:Foo foo = mock(Foo.class);Bar bar = mock(Bar.class);when(foo.getBar()).thenReturn(bar);when(bar.getName()).thenReturn("deep");
15. 为下一步的断言捕获参数 (1.8版本之后)
Mockito以java代码风格的形式来验证参数值 : 即通过使用equals()函数。
这也是我们推荐用于参数匹配的方式,因为这样会使得测试代码更简单、简洁。
在某些情况下,当验证交互之后要检测真实的参数值时这将变得有用。例如 :
ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);//参数捕获verify(mock).doSomething(argument.capture());//使用equal断言assertEquals("John", argument.getValue().getName());
警告 : 我们建议在验证时使用ArgumentCaptor,而不是在打桩时。
在打桩时使用ArgumentCaptor会降低测试代码的可读性,因为captor是在断言(又称为 验证/then)代码块之外创建的。
另外,它可以降低本地化的缺点,因为如果测试桩函数没有被调用,那么参数就不会被捕获。
总之,ArgumentCaptor与自定义的参数匹配器相关。
这两种技术都能用于检测外部传递到Mock对象的参数。
然而,使用ArgumentCaptor在以下的情况下更合适 :
自定义参数匹配器不能被重用
你仅需要断言参数值
16. 真实的局部模拟对象(mocks) (1.8版本之后)
在 Mockito 1.8 之前,spy() 方法并不会产生真正的局部mocks,而这无疑会让一些开发者困惑。
//你能用spy()方法创建局部模拟的对象:List list = spy(new LinkedList());
在一些特殊的情况下局部mocks是方便的:处理不能轻易修改的代码(第三方接口,临时重构的遗留代码等)。
在新的测试驱动和架构优秀的代码上,不要使用局部mocks的。
17. 重置mocks对象 (1.8版本之后)
通常情况下你不会需要重设你的mocks,只需要为每个测试方法重新创建一个mocks就可以了。
添加 reset() 方法的唯一原因是让它能与容器注入的mocks协作。
List mock = mock(List.class);when(mock.size()).thenReturn(10);mock.add(1);reset(mock);//在此刻,mock对象遗忘了所有的interactions & stubbing
18. 故障排查与验证框架的使用 (1.8版本之后)
FAQ: https://github.com/mockito/mockito/wiki/FAQ
mailing list: https://groups.google.com/group/mockito
validateLockitoUsage()显式验证框架状态,以检测Mockito的无效使用。
https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#validateMockitoUsage()
19.行为驱动开发的别名 (1.8版本之后)
用行为驱动开发的风格来写测试用例,会使用//given //when //then 注解作为测试方法的基础部分。
BDDMockito 类介绍了一个别名,使你的测试桩方法调用 BDDMockito.given(Object) 方法。
import static org.mockito.BDDMockito.*;Seller seller = mock(Seller.class);Shop shop = new Shop(seller);public void shouldBuyBread() throws Exception {//givengiven(seller.askForBread()).willReturn(new Bread());//whenGoods goods = shop.buyBread();//thenassertThat(goods, containBread());}
20. 可序列化的mocks(1.8.1版本之后)
模拟的对象是可以被序列化的。有了这个特性,你就可以在要求依赖项是可序列化的地方,使用模拟对象。
警告:这个特性应该在单元测试中少用。
用MockSettings.serializable():创建一个可序列化的mock对象:
List serializableMock = mock(List.class, withSettings().serializable());
spy()方法没有MockSettings的参数,可以用下面的方法创建spy对象
List<Object> list = new ArrayList<Object>();List<Object> spy = mock(ArrayList.class, withSettings().spiedInstance(list).defaultAnswer(CALLS_REAL_METHODS).serializable());
21. 新的注解 : @Captor,@Spy,@InjectMocks (1.8.3版本之后)
@Captor 简化 ArgumentCaptor 的创建 - 当需要捕获的参数是一个令人讨厌的泛型类,而且你想避免编译时警告。
@Spy - 你可以用它代替 spy(Object) 方法
@InjectMocks - 自动将模拟或监视的对象注入到被测试对象中。
需要注意的是 @InjectMocks 也能与 @Spy 一起使用,这就意味着 Mockito会在测试中,将mocks对象注入局部mock对象中。这变得很复杂,所以你还是应该少用局部mock。
所有新的注解都是只在MockitoAnnotations.initMocks(Object)被处理。
就像@Mock注解,你能用内置runner: MockitoJUnitRunner 或 规则: MockitoRule来开启。
22. 带超时的验证 (1.8.5版本之后)
这个特性应该少被使用 - 找到更好的方法去测试多线程系统。
还没有实现和 InOrder 验证协作。
//在100毫秒内调用someMethod时通过//校验通过时会马上结束(可能不会等到100ms)verify(mock, timeout(100)).someMethod();//上一行例子是下面这行代码的别名:verify(mock, timeout(100).times(1)).someMethod();//someMethod()在100ms内执行了2次就会马上通过verify(mock, timeout(100).times(2)).someMethod();//等效: someMethod()在100ms内执行了2次就会马上通过verify(mock, timeout(100).atLeast(2)).someMethod();
23. 自动实例化被@Spy, @InjectMocks注解的字段以及构造函数注入 (1.9.0版本之后)
Mockito 现在会通过构造器注入、setter注入 或字段注入方式,尽可能初始化带有 @Spy 和 @InjectMocks 注解的字段。
为了利用这个特性,你需要使用 MockitoAnnotations.initMocks(Object), MockitoJUnitRunner 或 MockitoRule。
//旧的写法:@Spy BeerDrinker drinker = new BeerDrinker();//新的写法:@Spy BeerDrinker drinker;//@InjectMocks注解也可以这么用:@InjectMocks LocalPub;
24. 单行测试桩 (1.9.0版本之后)
Mockito 现在允许在打桩时创建模拟对象。主要是,它允许通过一行代码来创建一个测试桩。这对保持代码的整洁很有用。
Car boringStubbedCar = when(mock(Car.class).shiftGear()).thenThrow(EngineNotStarted.class).getMock();
25. 验证被忽略的测试桩 (1.9.0版本之后)
Mockito 现在允许为了更好的验证而忽略已有的测试桩。
与 verifyNoMoreInteractions() 方法或验证 inOrder() 方法组合使用,有时很有用。
警告,ignoreStubs() 可能会导致 verifyNoMoreInteractions(ignoreStubs(...)) 方法的过度使用。
谨记在心,Mockito 没有推荐在每一个测试中用 verifyNoMoreInteractions() 方法
verify(mock).foo();verify(mockTwo).bar();//忽略所有已经打桩的方法:verifyNoMoreInvocations(ignoreStubs(mock, mockTwo));//创建将忽略打桩信息的InOrderInOrder inOrder = inOrder(ignoreStubs(mock, mockTwo));inOrder.verify(mock).foo();inOrder.verify(mockTwo).bar();inOrder.verifyNoMoreInteractions();
26. mock详情 (2.2.x中改进)
Mockito 提供API来查看mock对象的详情。这些API对高级用户和mock框架整合者很有用。
//识别特定的对象是一个mock对象或者是spy对象:Mockito.mockingDetails(someObject).isMock();Mockito.mockingDetails(someObject).isSpy();//获取mock对象的详情信息,包括它的类型或默认的answer:MockingDetails details = mockingDetails(mock);details.getMockCreationSettings().getTypeToMock();details.getMockCreationSettings().getDefaultAnswer();//获取mock对象的执行器(invocations)和打桩信息(stubbings):MockingDetails details = mockingDetails(mock);details.getInvocations();details.getStubbings();//打印所有的交互(including stubbing, unused stubs)System.out.println(mockingDetails(mock).printInvocations());
27. 真实实例的委托调用 (Since 1.9.5)
对于使用通常的spy API难以模拟或监控的对象有用。
从 Mockito 的 1.10.11 版本开始, delegate 有可能和 mock 的类型相同也可能不同。
如果不是同一类型,delegate 类型需要提供一个匹配方法否则就会抛出一个异常。
可能用到这个特性的案例:
带有 interface 的 final 类
已经自定义代理的对象
带有 finalize 方法的特殊对象,也就是避免执行2次
和常规 spy 的不同:
标准的 spy (spy(Object)) 包含被监视实例的所有状态信息,方法在 spy 对象上被调用。
被监视的对象只在 mock 创建时被用来拷贝状态信息。
如果你通过标准 spy 调用一个方法,这个 spy 会调用其内部的其他方法记录这次操作, 以便后面验证使用。等效于存根 (stubbed)操作。
代理方式的 mock 只是简单的把所有方法委托给 delegate。
delegate 一直被当成它所代理的方法使用。
如果你调用委托的mock对象上的方法,它会调用其内部的其他方法,这些调用不会被记录,打桩动作 在这里也不会生效。
委托方式的Mock 相对于标准的 spy 来说功能弱了很多,不过在标准 spy 不能被创建的时候这还是很有用。
final class DontYouDareToMockMe implements list { ... }DontYouDareToMockMe awesomeList = new DontYouDareToMockMe();List mock = mock(List.class, AdditionalAnswers.delegatesTo(awesomeList));//这样不行,因为会调用真实方法,会抛出IndexOutOfBoundsExceptionwhen(listWithDelegate.get(0)).thenReturn("foo");//要用doReturn打桩doReturn("foo").when(listWithDelegate).get(0);
28. MockMaker API (Since 1.9.5)
为了满足用谷歌Android用户的需求,Mockito 现在提供一个扩展点,允许替换代理生成引擎。默认情况下,Mockito 使用 Byte Buddy 创建动态代理。
这个扩展点是为想要扩展 Mockito 功能的高级用户准备的。比如,我们现在就可以在 dexmaker 的帮助下使用 Mockito 测试 Android。
原因和示例请看 MockMaker 的文档
29. BDD 风格的验证 (Since 1.10.0)
开始验证时,使用then关键字可以开启 Behavior Driven Development (BDD) 风格的验证。
given(dog.bark()).willReturn(2);// when...then(person).should(times(2)).ride(bike);
更多信息请查阅 BDDMockito.then(Object)
30. 监视 或 模拟 抽象类 (1.10.12版本加入,在2.7.13 和 2.7.14版特征得到增强))
现在可以方便的 spy 一个抽象类。注意,过度使用 spy 或许意味着代码的设计上有问题。
目前的话只支持无参构造函数。
//方便的API,新增重载的spy()方法:SomeAbstract spy = spy(SomeAbstract.class);//模拟抽象方法,监视接口默认方法(只有在2.7.13后才可用)Function function = spy(Function.class);//健壮的 API,通过配置构造器:OtherAbstract spy = mock(OtherAbstract.class, withSettings().useConstructor().defaultAnswer(CALLS_REAL_METHODS));//模拟构造器带参数的抽象类(只有在2.7.14后才可用)SomeAbstract spy = mock(SomeAbstract.class, withSettings().useConstructor("arg1", 123).defaultAnswer(CALLS_REAL_METHODS));//模拟非静态内部抽象类:InnerAbstract spy = mock(InnerAbstract.class, withSettings().useConstructor().outerInstance(outerInstance).defaultAnswer(CALLS_REAL_METHODS));
31. Mockito的模拟对象 可以通过 classloaders 序列化/反序列化 (Since 1.10.0)
Mockito 通过 classloader 引入序列化。和其他形式的序列化一样,所有 mock 层的对象类型都要可序列化, 包括 answers。
因为序列化模式需要大量的工作,所以这是一个可选配置。
// 常规的 serializationmock(Book.class, withSettings().serializable());// 通过 classloaders 序列化mock(Book.class, withSettings().serializable(ACROSS_CLASSLOADERS));
32. Deep stubs 更好的泛型支持 (Since 1.10.0)
深度打桩模式 现在可以更好的查找类的泛型信息。这就意味着像这样的类 不必去 mock 它的行为就可以使用。
class Lines extends List<Line> {// ...}lines = mock(Lines.class, RETURNS_DEEP_STUBS);//现在 Mockito 知道这是Line类型,而不是ObjectLine line = lines.iterator().next();
请注意,大多数情况下 mock 返回一个 mock 对象是错误的。
33. Mockito JUnit rule (Since 1.10.17)
Mockito 现在提供一个 JUnit 的 rule。
目前为止,有两种方法可以初始化 fields ,这些fields使用了 Mockito 提供的注解比如 @Mock, @Spy, @InjectMocks 等等。
用 @RunWith(@MockitoJUnitRunner.class) 标注 JUnit 测试类
在 @Before 方法中调用 MockitoAnnotations.initMocks(Object)
现在你可以选择使用一个 rule:
@RunWith(YetAnotherRunner.class)public class TheTest {@Rule public MockitoRule mockito = MockitoJUnit.rule();// ...}
34. 开启和关闭插件的开关 (Since 1.10.15)
这是一个测试特性,可以控制一个 mockito-plugin 开启或者关闭。
详情请查看 PluginSwitch
35. 自定义验证失败信息 (Since 2.1.0)
允许声明一个在验证失败时输出的自定义消息 示例:
// 在验证失败时,会打印自定义的消息verify(mock, description("This will print on failure")).someMethod();// 任何验证模式下都能使用这种方式verify(mock, times(2).description("someMethod should be called twice")).someMethod();
36. Java 8 Lambda匹配器的支持 (Since 2.1.0)
你可以在参数匹配器(ArgumentMatcher)上使用Java 8 lambda表达式,来减少对参数捕获器(ArgumentCaptor)的依赖。
如果你需要验证mock对象上方法调用的输入是正确的,那么你需要正常使用参数捕获器来找到使用过的操作数,并且之后对它们做断言。
写一个lambda来表示匹配关系是很容易的。你方法的参数使用argThat进行连接时,参数将作为强类型对象传给参数匹配器,所以通过这种方式能做任何事情。
// 验证一个list只添加了某些长度的字符串(字符串长度小于5)// 注意 - 这种写法只有在Java 8下能编译通过verify(list, times(2)).add(argThat(string -> string.length() < 5));// Java 8 下,更复杂的例子 - 你可以通过函数式指定复杂的验证行为verify(target, times(1)).receiveComplexObject(argThat(obj -> obj.getSubObject().get(0).equals("expected")));// lambda的方式也可以被用到:定义不同入参下,mock对象的行为// in this case if the input list was fewer than 3 items the mock returns nullwhen(mock.someMethod(argThat(list -> list.size()<3))).thenReturn(null);
37. Java 8 自定义Answer的支持 (Since 2.1.0)
Answer接口只有一个方法,Java 8 使用lambda表达式来实现它非常简单。
// answer每次都返回12doAnswer(invocation -> 12).when(mock).doSomething();// 用参数里的一个值作为返回值 - 转换成你想要的正确类型 doAnswer(invocation -> ((String)invocation.getArgument(1)).length()).when(mock).doSomething(anyString(), anyString(), anyString());
可以定义answer/actions,用被调用方法的参数作为answer/actions中lambda的入参。
它们依赖的相关answer接口在org.mockito.stubbing包下,Answer接口最多支持5个参数。
// Java 8 - style 1doAnswer(AdditionalAnswers.answerVoid((operand, callback) -> callback.receive("dummy")).when(mock).execute(anyString(), any(Callback.class));// Java 8 - style 2 - 假设AdditionAnswers已经静态导入doAnswer(answerVoid((String operand, Callback callback) -> callback.receive("dummy")).when(mock).execute(anyString(), any(Callback.class));// Java 8 - style 3 - 被模拟的方法是测试类的静态成员private static void dummyCallbackImpl(String operation, Callback callback) {callback.receive("dummy");}doAnswer(answerVoid(TestClass::dummyCallbackImpl).when(mock).execute(anyString(), any(Callback.class));
38. 元数据和泛型信息保留 (Since 2.1.0)
Mockito 现在会保留mock方法和类上的注解信息,也会保留泛型的元信息。
@MyAnnotationclass Foo {List<String> bar() { ... }}Class<?> mockType = mock(Foo.class).getClass();assert mockType.isAnnotationPresent(MyAnnotation.class);assert mockType.getDeclaredMethod("bar").getGenericReturnType() instanceof ParameterizedType;
使用 Java 8,Mockito 现在也保存类型注解(type annotations)。
这是默认行为,如果有可选择的MockMaker被使用,这种行为可能不会持续。
39. 模拟final类型,枚举 和 final方法 (Since 2.1.0)
Mockito 现在对模拟final类和方法提供一个孵化中的,可选的的支持。
这个可选的mock maker,它使用Java instrumentation API和子类两者结合的方式,而不是创建一个新的类来代表这个mock对象。通过这种方式,使模拟final类和方法成为了可能。
这个mock maker默认是关闭的,能通过mockito的扩展机制来被激活,只要在classpath上创建一个文件/mockito-extensions/org.mockito.plugins.MockMaker,文件包含mock-maker-inline这个值。
方便起见,Mockito团队提供了一个artifact,这个artifact里mock maker已经配置好。在项目中引入mockito-inline artifact来代替mockito-core artifact。
注意,等到对final类和方法的模拟功能集成到默认的mock maker后,这个artifact可能会停止。
关于这个mock maker几个需要注意的点是:
模拟final类和枚举的mock配置是不兼容的:
显示的序列化支持: withSettings().serializable()
特殊接口: withSettings().extraInterfaces()
一些方法不能被模拟:
java.*下的包可见方法
本地方法
这个mock maker被设计成围绕着Java Agent运行时的附件(attachment);这个要求一个兼容的JVM
当在Java 9之前的非JDK虚拟机上运行时,可以在启动JVM时使用-javaagent参数手动添加Byte Buddy Java代理jar。
40. “严格的”Mocktio能提高生产效率并使测试用例更清晰(2.+版本之后)
通过JUnit4 Rules来开启严格打桩 - MockitoRule.strictness(Strictness) with Strictness.STRICT_STUBS
通过JUnit4 Runner来开启严格打桩 - MockitoJUnitRunner.StrictStubs
通过JUnit5 Extension来开启严格打桩 - org.mockito.junit.jupiter.MockitoExtension
通过TestNG Listener来开启严格打桩 - MockitoTestNGListener
如果你不能使用runner/rule来开启严格打桩 - MockitoSession
通过MockitoJUnitRunner发现非必要测试桩
打桩参数不匹配的警告,记录在MockitoHint中
Mockito默认是一个“宽松的”模拟框架。有时让Mockito的测试用例难于被debug。
从2.1版本开始,Mockito拥有一些新的特性,这些特性推动框架走向“严格性”。
我们想让Mockito提供出色的调试能力,同时也不丢失它核心的模拟风格,针对直观性,清晰性和整洁的测试代码进行优化。
41. 框架集成的高级公开API (2.10.+版本之后)
它旨在用于需要用一些定制逻辑扩展或包装Mockito的其他测试工具和模拟框架。
新的 MockitoPlugins - 使框架集成者能够访问默认的Mockito插件。当需要实现自定义插件(例如MockMaker),并且将某些行为委托给默认Mockito的实现。
新的 MockSettings.build(Class) - 创建模拟配置的不可变的视图,供Mockito稍后使用。使用InvocationFactory创建invocations 或 当实现自定义MockHandler时,视图将非常有用。
新的 MockingDetails.getMockHandler() - 其他框架可能使用这个mock处理器,以编程方式对mock对象模拟调用。
新的 MockHandler.getMockSettings() - 用于获取创建模拟对象的配置。
新的 InvocationFactory - 提供创建调用对象实例的方法。有益于需要以编程方式模拟在mock对象上的方法调用的框架集成。
新的 MockHandler.getInvocationContainer() - 提供获取没有方法的调用容器对象(makrer接口)。容器需要隐藏它内部的实现机制,并且避免将其泄露给公共API。
改变Stubbing接口 - Stubbing现在继承自 Answer 接口。它是向后兼容的,因为Stubbing接口不是一个可扩展的(看@NotExtensible注解)。这个改变对用户来说是无感的。
NotExtensible - 公共的注解,指示用户不应该对提供给定类型的自定义实现。帮助框架集成者和我们的用户理解怎么去安全的使用Mockito API。
42. 用于集成的新API: 监听验证开始(verification start)事件(2.11.+版本之后)
新的 VerificationStartedListener 和 VerificationStartedEvent 使框架集成者替换模拟对象以进行验证。主要的驱动用例是Spring Boot的集成。
新的公共方法MockSettings.verificationStartedListeners(VerificationStartedListener...)允许在mock创建时,提供验证启动的监听器。
新的方便的方法MockingDetails.getMock()被添加,用来使MockingDetails API更加完整。
43. 用于集成的新API: 测试框架支持MockitoSession(2.15.+版本之后)
MockitoSessionBuilder and MockitoSession得到增强,以便通过测试框架集成实现重用(例如MockitoRule对于JUnit一样)。
MockitoSessionBuilder.initMocks(Object...)
允许传入多个测试类的实例,用来初始化被Mockito注解(例如@Mock)注释的字段。
当测试使用多个(例如内嵌的)测试类实例时,这个方法对高级框架集成非常有用(例如 JUnit Jupiter)。
MockitoSessionBuilder.name(String)
允许将名称从测试框架传递到MockitoSession,当使用Strictness.WARN时,MockitoSession将用于打印警告。
MockitoSessionBuilder.logger(MockitoSessionLogger)
使定制,用于完成模拟时产生的提示/告警 的logger成为了可能(对于测试和连接JUnit Jupiter等测试框架提供的报告功能很有用)。
MockitoSession.setStrictness(Strictness)
允许针对一次性场景改变MockitoSession的严格性,
例如,它可以为一个类下的所有测试用例设置一个默认的严格性,但是也可以改变单个或几个测试用例的严格性。
MockitoSession.finishMocking(Throwable)
避免由于存在多个相互竞争的失败而可能出现的混淆。
当提供的失败用例不是null时,它会关闭某些检查。
44. org.mockito.plugins.InstantiatorProvider泄露内部API所以被org.mockito.plugins.InstantiatorProvider2替代
45. 新的JUnit Jupiter (JUnit5+) extension
想要和JUnit Jupiter(JUnit5+)集成,可以使用org.mockito:mockito-junit-jupiter artifact。
请查阅MockitoExtension的Java文档。
46. 新的Mockito.lenient()和MockSettings.lenient()方法(2.20.0版本之后)
在Mockito2早期开始,就有严格打桩的特性。它非常有用,因为它驱动更整洁的测试用例和提供生产率。
严格打桩上报非必要的桩点,检查打桩参数不匹配 并且使测试用例更DRY(Strictness.STRICT_STUBS)。
在某些情况,你可能从严格打桩得到错误的否定。
你可以将特定打桩配置为宽松的,而所有其他的打桩和mocks使用严格打桩:
lenient().when(mock.foo()).thenReturn("ok");
如果你想在给定的mock对象的所有打桩都是宽松的,你可以按照下面的方式配置mock对象:
Foo mock = Mockito.mock(Foo.class, withSettings().lenient());
47. 用于在内联mocking中清除mock状态的新API(2.25.0版本之后)
在某些特定的、罕见的场景中,内联mocking会导致内存泄漏。没有干净的方法可以完全缓解这个问题。
因此,我们引入了一个新的API来显式清除mock状态(只有在内联mocking中才有意义!)。
public class ExampleTest {@Afterpublic void clearMocks() {Mockito.framework().clearInlineMocks();}@Testpublic void someTest() {...}}
48. 模拟静态方法(3.4.0版本之后)
当使用内联mock maker时,可以在当前线程和用户定义的范围内模拟静态方法调用。
通过这种方式,Mockito可以确保并发和顺序运行的测试不会产生干扰。
为了确保静态mock保持临时性,建议在try-with-resources构造中定义作用域。
在下面的例子中,除非被模拟,否则Foo类型的静态方法将返回Foo:
assertEquals("foo", Foo.method());try (MockedStatic mocked = mockStatic(Foo.class)) {mocked.when(Foo::method).thenReturn("bar");assertEquals("bar", Foo.method());mocked.verify(Foo::method);}assertEquals("foo", Foo.method());
由于静态mock定义了作用域,因此一旦释放作用域,它就会返回到原来的行为。要定义mock行为并验证静态方法调用,请使用返回的MockedStatic。
49. 模拟对象构造(3.5.0版本之后)
当使用内联mock maker时,可以在当前线程和用户定义的范围内对构造函数调用生成mock。
通过这种方式,Mockito可以确保并发和顺序运行的测试不会产生干扰。
为了确保构造函数mock保持临时性,建议在try-with-resources构造中定义作用域。
在以下示例中,Foo类型的构造将生成一个mock:
assertEquals("foo", new Foo().method());try (MockedConstruction mocked = mockConstruction(Foo.class)) {Foo foo = new Foo();when(foo.method()).thenReturn("bar");assertEquals("bar", foo.method());verify(foo).method();}assertEquals("foo", new Foo().method());
由于模拟构造的定义范围,一旦释放范围,对象构造就会返回到其原始行为。要定义mock行为并验证方法调用,请使用返回的MockedConstruction。
50. 仅模拟接口时避免代码生成(3.12.2版本之后)
JVM提供了代理功能,用于创建接口类型的动态代理。
对于大多数应用程序,Mockito必须能够模拟默认mock maker所支持的类,甚至能够模拟内联mock maker支持的最终类。
为了创建这样的mock,Mockito需要设置不同的JVM设施,并且必须应用代码生成。
如果只模拟接口,则可以选择使用基于代理API的org.mockito.internal.creation.proxy.ProxyMockMaker,这避免了其他模拟生成器的不同开销,但也限制了对接口的模拟。
这个mock-maker可以通过mockito扩展机制显式激活,只需在类路径中创建一个文件/mmockito-extensions/org.mockito.plugins.MockMaker,其中包含mock-maker-proxy值。
51. 把类标记为不可模拟(4.1.0版本之后)
可以使用@DoNotMock注解标记不能模拟的类或接口。
52. @Mock注解和MockSettings.strictness()方法的新strictness属性(4.6.0版本之后)
你可以将特定模拟配置为宽松的,而所有其他的模拟使用严格的:
@Mock(strictness = Strictness.LENIENT)Foo mock;// using MockSettings.withSettings()Foo mock = Mockito.mock(Foo.class, withSettings().strictness(Strictness.WARN));
53. 指定单个mock的mock maker(4.8.0版本之后)
// using annotation@Mock(mockMaker = MockMakers.SUBCLASS)Foo mock;// using MockSettings.withSettings()Foo mock = Mockito.mock(Foo.class, withSettings().mockMaker(MockMakers.SUBCLASS));
54. mock/spy不指定类(4.9.0版本之后)
Mockito将自动检测所需的类。
Foo foo = Mockito.mock();Bar bar = Mockito.spy();
相关文章:

Mockito5.2.0学习
Mockito是mocking框架,它让你用简洁的API做测试。而且Mockito简单易学,它可读性强和验证语法简洁。 Mockito 是一个针对 Java 的单元测试模拟框架,它与 EasyMock 和 jMock 很相似,都是为了简化单元测试过程中测试上下文 ( 或者称之…...

用指针实现内存动态分配
导引:已知:变量在使用前必须被定义且安排好存储空间。且变量有这么一些分类:全局变量、静态局部变量【它们的储存一般是在编译时确定,在程序开始执行前完成。】自动变量【在执行进入变量定义所在的复合语句时为它们分配存储&#…...

DBSCAN聚类算法及Python实现
DBSCAN聚类算法 DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一种基于密度的聚类算法,可以将数据点分成不同的簇,并且能够识别噪声点(不属于任何簇的点)。 DBSCAN聚类算法的基…...

风光及负荷多场景随机生成与缩减
目录 1 主要内容 计算模型 场景生成与聚类方法应用 2 部分程序 3 程序结果 4 程序链接 1 主要内容 该程序方法复现了《融合多场景分析的交直流混合微电网多时间尺度随机优化调度策略》3.1节基于多场景技术的随机性建模部分,该部分是随机优化调度的重要组成部分…...

lamda表达式
lamda表达式一. lamda表达式的特性二.常用匿名函数式接口2.1 Supplier接口2.2 Consumer接口2.3 Predicate接口2.4 Function接口2.5 BiFunction接口三.stream流传递先后顺序四.表达式4.1 ForEach4.2 Collect4.3 Filter4.4 Map4.5 MapToInt4.6 Distinct4.7 Sorted4.8 groupingBy4…...

MobTech 秒验|极速验证,拉新无忧
一、运营拓展新用户的难题 运营拓展新用户是每个应用都需要面对的问题,但是在实际操作中,往往会遇到一些困难。其中一个主要的难题就是注册和登录的繁琐性。用户在使用一个新的应用时,通常需要填写手机号、获取验证码、输入验证码等步骤&…...

大模型混战,阿里百度华为谁将成就AI时代的“新地基”?
从算力基础到用户生态,群雄逐鹿大模型 自2022年stable diffusion模型的进步推动AIGC的快速发展后,年底,ChatGPT以“破圈者”的姿态,快速“吸粉”亿万,在全球范围内掀起了一股AI浪潮,也促使了众多海外巨头竞…...

干翻Hadoop系列之:Hadoop前瞻之分布式知识
前言 一:海量数据价值 二:海量数据两个棘手问题 1:海量数据如何存储? 掌握分布式存储数据的思想。 A:方案1:单机存储磁盘不够加磁盘 限制问题: 1:一台计算机不能无限制拓充 2&a…...

MAE论文阅读《Masked Autoencoders Are Scalable Vision Learners》
文章目录动机方法写作方面参考Paper: https://arxiv.org/pdf/2111.06377.pdf 动机 首先简要介绍下BERT,NLP领域的BERT是基于Transformer架构,并采取无监督预训练的方式去训练模型。它提出的预训练方法在本质上是一种masked autoencoding,也就…...

代码随想录算法训练营第三十四天-贪心算法3| 1005.K次取反后最大化的数组和 134. 加油站 135. 分发糖果
1005. Maximize Sum Of Array After K Negations 参考视频:贪心算法,这不就是常识?还能叫贪心?LeetCode:1005.K次取反后最大化的数组和_哔哩哔哩_bilibili 贪心🔍 的思路,局部最优ÿ…...

比较系统的学习 pandas (2)
pandas 数据读取与输出方法和常用参数 1、读取 CSV文件 pd.read_csv("pathname",step,encoding"gbk",header"infer",name[],skip_blank_linesTrue,commentNone) path : 文件路径 step : 指定分隔符,默认为 逗号 enco…...

怎么查看电脑主板最大支持多少内存?
很多电脑,内存不够用,但应速度慢;还有一些就是买了很大的内存条,但是还是反应慢;这是为什么呢?我今天明白了,原来每个电脑都有自己的适配内存,就是每个电脑能支持多大的内存…...

数据结构——线段树
线段树的结构 线段树是一棵二叉树,其结点是一条“线段”——[a,b],它的左儿子和右儿子分别是这条线段的左半段和右半段,即[a, (ab)/2 ]和[(ab)/2 ,b]。线段树的叶子结点是长度为1的单位线段[a,a1]。下图就是一棵根为[1,10]的线段树࿱…...

【C++进阶】实现C++线程池
文章目录1. thread_pool.h2. main.cpp1. thread_pool.h #pragma once #include <iostream> #include <vector> #include <queue> #include <thread> #include <mutex> #include <condition_variable> #include <future> #include &…...

Redis常用五种数据类型
一、Redis String字符串 1.简介 String类型在redis中最常见的一种类型 string类型是二制安全的,可以存放字符串、数值、json、图像数据 value存储最大数据量是512M 2. 常用命令 set < key>< value>:添加键值对 nx:当数据库中…...

C++ Primer第五版_第十一章习题答案(1~10)
文章目录练习11.1练习11.2练习11.3练习11.4练习11.5练习11.6练习11.7练习11.8练习11.9练习11.10练习11.1 描述map 和 vector 的不同。 map 是关联容器, vector 是顺序容器。 练习11.2 分别给出最适合使用 list、vector、deque、map以及set的例子。 list:…...

GEE:使用LandTrendr进行森林变化检测详解
作者:_养乐多_ 本文介绍了一段用于地表变化监测的代码,该代码主要使用谷歌地球引擎(GEE)中的 Landsat 时间序列数据,采用了 Kennedy 等人(2010) 发布的 LandTrendr 算法,对植被指数进行分割,通过计算不同时间段内植被指数的变化来检测植被变化。 目录 一、加入矢量边界 …...
docker项目实施
鲲鹏916架构openEuler-arm64成功安装docker并跑通tomcat容器_闭关苦炼内功的技术博客_51CTO博客鲲鹏916架构openEuler-arm64成功安装docker并跑通tomcat容器,本文是基于之前这篇文章鲲鹏920架构arm64版本centos7安装docker下面开始先来看下系统版本卸载旧版本旧版本…...

springboot实现邮箱验证码功能
引言 邮箱验证码是一个常见的功能,常用于邮箱绑定、修改密码等操作上,这里我演示一下如何使用springboot实现验证码的发送功能; 这里用qq邮箱进行演示,其他都差不多; 准备工作 首先要在设置->账户中开启邮箱POP…...

Java 进阶(5) Java IO流
⼀、File类 概念:代表物理盘符中的⼀个⽂件或者⽂件夹。 常见方法: 方法名 描述 createNewFile() 创建⼀个新文件。 mkdir() 创建⼀个新⽬录。 delete() 删除⽂件或空⽬录。 exists() 判断File对象所对象所代表的对象是否存在。 getAbsolute…...

“终于我从字节离职了...“一个年薪40W的测试工程师的自白...
”我递上了我的辞职信,不是因为公司给的不多,也不是因为公司待我不好,但是我觉得,我每天看中我憔悴的面容,每天晚上拖着疲惫的身体躺在床上,我都不知道人生的意义,是赚钱吗?是为了更…...

设计模式之策略模式(C++)
作者:翟天保Steven 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 一、策略模式是什么? 策略模式是一种行为型的软件设计模式,针对某个行为,在不同的应用场景下&…...

从工厂普工到Python女程序员,聊聊这一路我是如何逆袭的?
我来聊聊我是如何从一名工厂普工,到国外程序员的过程,这里面充满了坎坷。过去我的工作是在工厂的流水线上,我负责检测电池的正负极。现如今我每天从早上6:20起床,6点四五十分出发到地铁站,7:40到公司。我会给自己准备一…...

全国青少年信息素养大赛2023年python·选做题模拟二卷
目录 打印真题文章进行做题: 全国青少年电子信息智能创新大赛 python选做题模拟二卷 一、单选题 1. numbers = [1, 11, 111, 9], 运行numbers.sort() 后,运行numbers.reverse() numbers会变成?( )...

分布式事务Seata原理
Seata 是一款开源的分布式事务解决方案,致力于提供高性能与简单易用的分布式事务服务,为用户提供了 AT、TCC、SAGA 和 XA 几种不同的事务模式。Seata AT模式是基于XA事务演进而来,需要数据库支持。AT 模式的特点就是对业务无入侵式࿰…...

用ChatGPT怎么赚钱?普通人用这5个方法也能赚到生活费
ChatGPT在互联网火得一塌糊涂,因为它可以帮很多人解决问题。比如:帮编辑人员写文章,还可以替代程序员写代码,帮策划人员写文案策划等等。ChatGPT这么厉害,能否用它来赚钱呢?今天和大家分享用ChatGPT赚钱的5…...

( “树” 之 DFS) 110. 平衡二叉树 ——【Leetcode每日一题】
110. 平衡二叉树 给定一个二叉树,判断它是否是高度平衡的二叉树。 本题中,一棵高度平衡二叉树定义为: 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。 示例 1: 输入:root [3,9,20,null,null,15,7] …...

nvm软件使用-同一个环境下控制多个不同node版本
1.使用场景 nvm是一个用于管理Node.js版本的工具,它可以让你在同一台机器上安装和切换不同的Node.js版本。使用nvm的好处有以下几点: 1.1.nvm可以让你轻松地测试你的代码在不同的Node.js版本下的兼容性和性能,避免因为版本差异导致的问题。…...

连续两个南航的研究生面试出了从来没出现过的问题,本科和研究生都是计算机专业的,竟然说static是不可更改的。
最近面试人数有点多,面试有点频繁,因此发现了一些学生普遍会发生的错误,可以说是很离谱。 因为做了十多年的面试官,无论是大中小厂的面试,还是社招、校招。 从来没有遇到过这样的情况,而且发生在两个南航…...

How to install nacos/nacos-server:v2.1.2-slim with docker
今天给大家介绍一下如何基于Docker的nacos/nacos-server:v2.1.2-slim镜像安装nacos 1、Data Source 我们需要从nacos的github官网下载nacos 2.12发布包 nacos-server-2.1.2.tar.gznacos-server-2.1.2.zip 这里以nacos-server-2.1.2.tar.gz为例来介绍,解压后我们…...