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());
相关推荐
夏天的味道٥3 小时前
使用 Java 执行 SQL 语句和存储过程
java·开发语言·sql
冰糖码奇朵5 小时前
大数据表高效导入导出解决方案,mysql数据库LOAD DATA命令和INTO OUTFILE命令详解
java·数据库·sql·mysql
好教员好5 小时前
【Spring】整合【SpringMVC】
java·spring
浪九天6 小时前
Java直通车系列13【Spring MVC】(Spring MVC常用注解)
java·后端·spring
堕落年代6 小时前
Maven匹配机制和仓库库设置
java·maven
功德+n6 小时前
Maven 使用指南:基础 + 进阶 + 高级用法
java·开发语言·maven
香精煎鱼香翅捞饭7 小时前
java通用自研接口限流组件
java·开发语言
ChinaRainbowSea7 小时前
Linux: Centos7 Cannot find a valid baseurl for repo: base/7/x86_64 解决方案
java·linux·运维·服务器·docker·架构
囧囧 O_o7 小时前
Java 实现 Oracle 的 MONTHS_BETWEEN 函数
java·oracle
去看日出7 小时前
RabbitMQ消息队列中间件安装部署教程(Windows)-2025最新版详细图文教程(附所需安装包)
java·windows·中间件·消息队列·rabbitmq