Mockito 调用模拟对象全解析

一个人最大的缺点,不是自私、多情、野蛮、任性,而是偏执地爱一个不爱自己的人

Spring Boot框架来编写一个简单的示例,演示如何使用DI框架在单元测试中使用Mock实现,避免对外部环境的依赖。

首先,我们创建一个UserService接口,定义了一个getUserById方法用于查询用户信息:

public interface UserService {
    User getUserById(long userId);
}

接着,我们创建一个UserServiceImpl实现类,实现了getUserById方法:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    public User getUserById(long userId) {
        return userDao.getUserById(userId);
    }
}

在这个实现类中,我们注入了一个UserDao对象,用于从数据库中查询用户信息。这里我们假设UserDao已经实现并可用。

现在,我们需要编写一个单元测试来测试getUserById方法。由于这个方法依赖于UserDao对象,为了避免对外部环境的依赖,我们可以使用Mockito框架来模拟UserDao对象的行为。

以下是使用Mockito框架编写的单元测试代码:

@RunWith(SpringRunner.class)
public class UserServiceTest {

    @MockBean
    private UserDao userDao;

    @Autowired
    private UserService userService;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testGetUserById() {
        User mockUser = new User();
        mockUser.setId(1L);
        mockUser.setUsername("test");
        mockUser.setPassword("test");
        Mockito.when(userDao.getUserById(1L)).thenReturn(mockUser);

        User user = userService.getUserById(1L);
        Assert.assertEquals("test", user.getUsername());
        Assert.assertEquals("test", user.getPassword());
    }
}

在这个测试中,我们使用@MockBean注解来注入一个Mock的UserDao对象,并使用Mockito.when方法模拟getUserById方法的行为。这样,在测试中就可以使用MockUserDao对象,而不需要依赖于外部环境。

通过这种方式,我们可以编写稳健、可靠的单元测试,并将其纳入持续集成系统中进行自动化测试,提高软件的质量和稳定性。同时,我们也能避免对外部环境的依赖,保证单元测试的独立性和可重复性。

关键代码分析

MockitoAnnotations.initMocks(this)

MockitoAnnotations.initMocks(this)方法并不会产生代理类,它主要是用于初始化Mockito注解。在测试中,我们通常使用@Mock、@Spy、@InjectMocks等注解来创建Mock对象,并使用Mockito.when、Mockito.verify等方法来模拟对象的行为和验证方法调用。

但是,如果我们不调用MockitoAnnotations.initMocks(this)方法,这些Mock对象就无法被正确初始化,从而导致测试失败。因此,我们通常在@Before注解方法中调用这个方法,以确保所有的Mock对象都已经被正确初始化。

在具体实现中,MockitoAnnotations.initMocks(this)方法会扫描测试类中所有的@Mock、@Spy、@InjectMocks注解,并根据注解中的类型和名称来创建对应的Mock对象,并将这些对象注入到测试类中。这样,在测试过程中就可以使用这些Mock对象来模拟外部依赖,从而实现单元测试的独立性和可重复性。

Mockito.when(userDao.getUserById(1L)).thenReturn(mockUser);

Mockito.when(userDao.getUserById(1L)).thenReturn(mockUser)这行代码的作用是使用Mockito框架来模拟userDao.getUserById方法的行为。

具体来说,Mockito.when方法会返回一个Mockito的OngoingStubbing对象,用于进一步指定模拟方法的返回值或抛出异常。在这个例子中,我们使用thenReturn方法来指定当userDao.getUserById(1L)方法被调用时,返回一个预先构造好的mockUser对象。

当测试中调用userService.getUserById(1L)方法时,Mockito框架会捕获这个方法调用,并返回预先定义的mockUser对象,从而避免对外部环境的依赖,保证单元测试的独立性。

这里还要补充一点,Mockito.when方法并不是真正的模拟方法调用,它只是记录了一个Stubbing规则,告诉Mockito在测试运行过程中如何处理这个方法调用。只有当真正调用模拟的方法时,Mockito才会根据Stubbing规则返回预先定义的值或抛出异常。

在具体实现中,Mockito.when方法会根据指定的方法调用和参数,生成一个MethodInvocation对象,用于表示这个方法调用的信息。然后,Mockito会将这个MethodInvocation对象和Stubbing规则保存到一个全局的InvocationContainer中,用于在测试过程中捕获和处理对这个方法的调用。

Mockito 怎么知道我的 UserService 调用到了我的 UserDao 方法了呢?

Mockito能够知道UserService是否调用了UserDao方法,是通过代理对象来实现的。

在这个示例中,我们使用了Spring的依赖注入(DI)框架来注入UserService和UserDao对象,使得UserService依赖于UserDao对象。在测试中,我们使用Mockito来创建一个Mock的UserDao对象,并将这个对象注入到UserService中。由于UserService中的UserDao对象被Mockito替换成了Mock对象,我们就能够捕获UserService对UserDao方法的调用。

具体来说,当我们调用UserService中的getUserById方法时,实际上是调用了Mockito所创建的一个代理对象的getUserById方法。这个代理对象能够捕获所有的方法调用信息,并将这些信息保存到Mockito的InvocationContainer中。然后,Mockito根据这些调用信息来判断是否需要进行Stubbing,即模拟方法调用的返回值或抛出异常。

当我们使用Mockito.when方法来定义UserDao的getUserById方法的返回值时,Mockito就会根据InvocationContainer中的调用信息来确定是否应用这个Stubbing规则。如果当前的调用信息和Mockito.when方法定义的规则匹配,Mockito就会返回预定义的值或抛出预定义的异常。

因此,Mockito能够知道UserService是否调用了UserDao方法,是通过代理对象捕获所有的方法调用信息,并根据这些信息来判断是否需要进行Stubbing来实现的。

Mockito 调用模拟对象全解析

  1. 使用Java反射机制创建一个代理对象,代理对象的类型是Mock对象的接口或类。
  2. 代理对象能够记录Mock对象的所有方法调用信息,并将这些信息保存到一个全局的InvocationContainer对象中。
  3. 在使用Mockito.when方法定义Mock对象的方法行为时,Mockito会将这些Stubbing规则保存到InvocationContainer对象中,以便在测试过程中捕获和处理方法调用。
  4. 在测试过程中,当我们调用Mock对象的方法时,代理对象会捕获这个方法调用信息,Mockito会根据InvocationContainer中保存的所有Invocation对象来匹配相应的Stubbing规则,并返回预定义的值或抛出预定义的异常。
  5. 在测试结束时,我们可以使用Mockito.verify方法来验证Mock对象的方法调用是否符合预期,从而实现单元测试的有效性和可靠性。

因此,Mockito能够调用到我们通过when注册到InvocationContainer的调用信息,是通过代理对象捕获方法调用信息,并将它们保存到InvocationContainer中实现的。在测试过程中,我们可以使用Mockito提供的各种方法来定义Mock对象的行为和验证方法调用,从而实现单元测试的独立性和可重复性。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用Mockito模拟ThreadPoolExecutor对象可以通过以下步骤实现: 1. 导入Mockito库:在项目中引入Mockito库,可以通过Maven或Gradle等构建工具添加依赖。 2. 创建ThreadPoolExecutor对象模拟实例:使用Mockito的`mock()`方法创建一个ThreadPoolExecutor对象模拟实例。 ```java ThreadPoolExecutor executorMock = Mockito.mock(ThreadPoolExecutor.class); ``` 3. 设置模拟对象的行为:使用Mockito的`when().thenReturn()`方法设置模拟对象的方法调用行为。 ```java Mockito.when(executorMock.submit(Mockito.any(Runnable.class))).thenAnswer(invocation -> { Runnable task = invocation.getArgument(0); task.run(); return null; }); ``` 在上述示例中,我们使用`when().thenReturn()`方法来模拟ThreadPoolExecutor的`submit()`方法,当调用`submit()`方法时,我们获取传递给该方法的Runnable对象,并直接运行它。 4. 使用模拟对象进行测试:将模拟的ThreadPoolExecutor对象传递给需要测试的代码,并进行相应的测试。 ```java // 示例代码 ThreadPoolExecutor executor = executorMock; // 执行需要测试的代码 // ... ``` 至于在ThreadPoolExecutor重复调用dao的写法,可以按照以下步骤进行: 1. 创建一个实现了Runnable接口的任务类,该任务类负责执行需要重复调用的dao操作。 ```java public class DaoTask implements Runnable { private Dao dao; public DaoTask(Dao dao) { this.dao = dao; } @Override public void run() { // 执行dao操作 dao.doSomething(); } } ``` 2. 创建ThreadPoolExecutor对象,并提交多个DaoTask任务。 ```java ThreadPoolExecutor executor = new ThreadPoolExecutor(...); Dao dao = new Dao(); for (int i = 0; i < numberOfTasks; i++) { executor.submit(new DaoTask(dao)); } ``` 在上述示例中,我们创建了一个ThreadPoolExecutor对象,并使用循环提交了多个DaoTask任务,每个任务都会执行dao的操作。 3. 关闭ThreadPoolExecutor。 ```java executor.shutdown(); ``` 在所有任务执行完毕后,需要调用`shutdown()`方法来关闭ThreadPoolExecutor。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值