Mockito入门和使用场景探究

背景

Mockito是一个流行的Java测试框架,它通过提供简洁的API来帮助开发者编写单元测试。以下是Mockito的主要特点和用法:

  • 创建模拟对象:使用Mockito可以轻松地创建模拟对象,这些对象可以模拟真实对象的行为,用于替代真实对象的依赖项,以便进行独立的单元测试。

  • 部分模拟对象:Mockito还允许创建部分模拟对象,即spy对象。Spy对象保留了真实对象的部分行为,同时可以模拟方法的返回值。

  • 依赖注入:Mockito提供了@InjectMocks注解,用于自动注入模拟对象到待测试的类中,简化了测试代码的编写过程。

  • 验证行为:Mockito提供了丰富的方法来验证对象的行为,确保在测试过程中对象的方法被正确地调用。

  • 降低耦合度:通过使用Mockito,可以虚拟出外部依赖,降低测试组件之间的耦合度,使测试更加专注于代码的流程与结果。

  • 创建方式:Mockito提供了多种创建模拟对象的方式,包括使用Mockito.mock()方法或@Mock注解。这两种方式都可以有效地创建所需的模拟对象。

总的来说,Mockito通过提供强大的功能和方法,极大地简化了Java项目中单元测试的编写,使得开发者能够更加高效地进行测试驱动的开发。

使用场景

Mockito的使用场景包括但不限于以下几点:

  • 单元测试:在单元测试中,Mockito可以用来模拟复杂的依赖关系,确保测试的独立性和可靠性。通过模拟依赖对象的行为,可以专注于测试目标类的逻辑,而不受外部因素影响。

    集成测试:在集成测试中,Mockito可以用来模拟外部系统或服务的行为,这样可以在不实际调用外部系统的情况下验证系统的集成逻辑。

  • 系统测试:在系统测试中,Mockito可以用来模拟用户行为或特定的系统事件,以便在控制的环境下测试系统的整体行为。

  • 团队并行开发:当前后端或不同后端服务之间需要并行开发时,Mockito可以帮助开发者通过模拟接口来独立工作,从而加快开发进度并减少等待时间。

  • 测试驱动开发(TDD):在TDD实践中,开发人员在编写实际代码之前先编写测试用例。有了Mockito,即使某些功能尚未实现,也可以提前创建测试用例,因为可以模拟尚未完成的部分。

  • 隔离系统:当测试需要调用可能会对系统产生副作用的操作(如修改数据库)时,可以使用Mockito来创建一个虚拟的操作,避免对实际系统造成影响。

    模拟访问资源:如果需要测试的代码依赖于访问外部资源(如网络请求),使用Mockito可以模拟这些资源,避免因外部因素导致的测试不稳定。

  • 提高测试覆盖度:对于一些难以触发的场景(如服务器错误),可以使用Mockito来模拟这些情况,以确保测试用例的全面覆盖。

使用示例

@Mock注解

  • 最小化重复的模拟创建代码。
  • 使测试类更具可读性。
  • 使验证错误更容易读取,因为字段名称用于标识mock。
java 复制代码
public class ArticleManagerTest {

       @Mock private ArticleCalculator calculator;
       @Mock private ArticleDatabase database;
       @Mock private UserProvider userProvider;

       private ArticleManager manager;

       @org.junit.jupiter.api.Test
       void testSomethingInJunit5(@Mock ArticleDatabase database) {

带有回调的存根

允许使用通用Answer接口进行存根处理。这里有一个示例:thenReturn()thenThrow()

java 复制代码
 when(mock.someMethod(anyString())).thenAnswer(
     new Answer() {
         public Object answer(InvocationOnMock invocation) {
             Object[] args = invocation.getArguments();
             Object mock = invocation.getMock();
             return "called with arguments: " + Arrays.toString(args);
         }
 });

 //Following prints "called with arguments: [foo]"
 System.out.println(mock.someMethod("foo"));

方法返回结果

对于任何方法,都可以使用doThrow(), doAnswer(), doNothing(), doReturn() 和doCallRealMethod()来代替带有when()的相应调用。

java 复制代码
 doThrow(new RuntimeException()).when(mockedList).clear();

   //following throws RuntimeException:
   mockedList.clear();
 

spy

Mockito中的spy对象是一种对真实对象进行部分模拟的方式,它允许在不改变原有类行为的基础上,对特定方法进行监控和验证。

使用spy对象的目的是为了在不完全模拟一个对象的同时,能够对某些特定的行为进行监控或者验证。以下是spy对象与mock对象的区别:

  • 默认行为:对于未指定的方法,spy对象会调用真实的方法并返回真实的值,而mock对象则不执行任何操作,对于有返回值的方法,默认返回null。

  • 使用方式:在spy对象中,通常使用doReturn...when语句来指定特定方法的行为,这样可以确保真实的方法不被执行。而在mock对象中,使用when...thenReturn语句来定义方法的行为,这样私有方法不会被执行。

  • 覆盖率统计:当使用spy对象时,原有的方法仍然会被执行,因此可以统计到代码覆盖率。而shadow对象则无法被统计覆盖率,因为它完全替换了原有对象的行为。

java 复制代码
  List list = new LinkedList();
   List spy = spy(list);

   //可以选择截取一些方法:
   when(spy.size()).thenReturn(100);

   //使用spy调用*real*方法
   spy.add("one");
   spy.add("two");

   //打印"一"-列表的第一个元素
   System.out.println(spy.get(0));

   //size()方法已被存根-已打印100
   System.out.println(spy.size());

   //可选,可以验证
   verify(spy).add("one");
   verify(spy).add("two");
 

模拟静态方法

当使用内联mock maker时,可以在当前线程和用户定义的范围内模拟静态方法调用。通过这种方式,Mockito确保并发和顺序运行的测试不会干扰。为了确保静态mock保持临时性,建议在try-with-resources构造中定义作用域。

java 复制代码
 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行为并验证方法调用,使用返回的MockedConstruction。

java 复制代码
 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());
相关推荐
天天扭码2 分钟前
五天SpringCloud计划——DAY1之mybatis-plus的使用
java·spring cloud·mybatis
程序猿小柒7 分钟前
leetcode hot100【LeetCode 4.寻找两个正序数组的中位数】java实现
java·算法·leetcode
不爱学习的YY酱44 分钟前
【操作系统不挂科】<CPU调度(13)>选择题(带答案与解析)
java·linux·前端·算法·操作系统
丁总学Java1 小时前
Maven项目打包,com.sun.tools.javac.processing
java·maven
kikyo哎哟喂1 小时前
Java 代理模式详解
java·开发语言·代理模式
duration~1 小时前
SpringAOP模拟实现
java·开发语言
小码ssim1 小时前
IDEA使用tips(LTS✍)
java·ide·intellij-idea
潜洋2 小时前
Spring Boot教程之五:在 IntelliJ IDEA 中运行第一个 Spring Boot 应用程序
java·spring boot·后端
暮志未晚Webgl2 小时前
109. UE5 GAS RPG 实现检查点的存档功能
android·java·ue5
小叶lr2 小时前
idea 配置 leetcode插件 代码模版
java·leetcode·intellij-idea