来一杯 Mojito, 哦, Mockito ️

关键代码分析MockitoAnnotations.initMocks(this)Mockito.when(userDao.getUserById(1L)).thenReturn(mockUser);

Mockito 怎么知道我的 UserService 调用到了我的 UserDao 方法了呢?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 调用模拟对象全解析

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

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

相关链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: