SpringBoot教程(15) JUnit5 + Mockito @InjectMocks @Mock Stubbing

一、Mock测试介绍

1. 什么是Mock测试

Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDBC 中的ResultSet 对象),用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法。

2. Mock测试的常规步骤

  • Mock:创建出待测试的Mock对象
  • Stubbing:指定它的行为,return特定值或者抛出特定异常。when().thenReturn()或when().thenThrow()等
  • Verify:验证结果
    很多文章把Stubbing翻译成“打桩”或者“存根”,其实这样的翻译只会让人费解,所以不翻译,直接叫Stubbing,对程序员来说更好理解。

二、引入Mockito包

Mockito是比较出名且方便的Mock工具,它的API都比较直观,易于理解。
如果普通Java项目,不依赖SpringBoot,则直接引入

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-all</artifactId>
    <version>3.9.0</version>
    <scope>test</scope>
</dependency>

如果是SpringBoot项目

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-test</artifactId>
	<scope>test</scope>
</dependency>

三、Mockito实践

0. 前提准备:待测试的业务代码

先创建几个非常简单的文件,这个结构大伙一看就懂。
在这里插入图片描述
DictTypeServiceImpl 的具体实现如下:
可以看到DictTypeServiceImpl中是依赖DictTypeDao的。

@Service
public class DictTypeServiceImpl extends CommonServiceImpl<DictType> implements DictTypeService {

	//这里依赖了DictTypeDao 
    @Autowired
    private DictTypeDao dictTypeDao;

    public DictType save(DictType dictType) {
        return dictTypeDao.save(dictType);
    }

    public void deleteById(String id){
        dictTypeDao.deleteById(id);
    }
    
    public Optional<DictType> getById(String id) {
        return dictTypeDao.findById(id);
    }

    public Iterable<DictType> listByQuery(QueryBuilder queryBuilder) {
        return search(queryBuilder, DictType.class);
    }

    public boolean existById(String id) {
        return dictTypeDao.existsById(id);
    }  
}

1. 第一步:Mock

举例说明:
此时我就想写UT测试Service层的代码,不测试Dao层的代码,那就需要虚拟一个Dao层的对象(Mock对象)给Service用。
比如模拟Dao层中getById(“1”)的返回值,指定返回一个我们new的对象,而不是让Dao去查询数据库。
代码如下:

//我们自己定义一个返回值
DictType dictType = new DictType();
dictType.setId("1");
dictType.setCode("code1");
dictType.setName("name1");
Optional<DictType> dictTypeOptional = Optional.of(dictType);

//Stubbing,指定当执行到findById("1")时,返回上面定义的值,而不是执行真正的代码去查数据库
when(dictTypeDao.findById("1")).thenReturn(dictTypeOptional);

写这个UT时,可以创建3种对象:被测试对象、Mock对象、Spy对象

  • 被测试对象:就是你要测试的类,本博客中指DictTypeServiceImpl的对象。
  • Mock对象:虚拟出的对象,被测试对象依赖Mock对象。因为DictTypeServiceImpl中依赖DictTypeDao。本博客中Mock对象指DictTypeDao的对象。
  • Spy对象:Spy对象是一种特殊的Mock对象,它也可以作为被测对象的依赖对象。此时它和Mock对象的最大的区别:Mock对象的方法如果没有被Stubbing,调用时会返回相应对象的空值,而Spy对象的方法被调用时则会调用真实的代码逻辑。

1.1 @InjectMocks: 创建被测试对象

@InjectMocks的作用和@Autowired比较类似,但是它的成员变量将被@Mock和@Spy注解的字段注入。
DictTypeServiceImpl 的定义如下:

@Service
public class DictTypeServiceImpl extends CommonServiceImpl<DictType> implements DictTypeService {

	//这里依赖了DictTypeDao 
    @Autowired
    private DictTypeDao dictTypeDao;
	
	//省略其他方法
}

测试类中用@InjectMocks修饰在DictTypeServiceImpl上,那么@Mock或者@Spy修饰的对象会注入到@InjectMocks修饰的对象里。
注意@InjectMocks修饰在实现类上,而不是DictTypeService接口层,这个和@Autowired有不同。
在这里插入图片描述

1.2 @Mock:创建Mock对象

被测试的DictTypeServiceImpl中代码

public Optional<DictType> getById(String id) {
      return dictTypeDao.findById(id);
}

用@Mock修饰在对象上,就可以实现Mock对象。

@SpringBootTest
public class MockitoWebTest {

    @InjectMocks
    DictTypeServiceImpl dictTypeService;

    @Mock
    private DictTypeDao dictTypeDao;

    @Test
    public void testMockObject() {

        //我们自己定义一个返回值
        DictType dictType = new DictType();
        dictType.setId("1");
        dictType.setCode("mock_code1");
        dictType.setName("mock_name1");
        Optional<DictType> dictTypeOptional = Optional.of(dictType);

        //Stubbing,指定当执行到findById("1")时,返回上面定义的值
        when(dictTypeDao.findById("1")).thenReturn(dictTypeOptional);

        Optional<DictType> dictTypeById_1 = dictTypeService.getById("1");
        Optional<DictType> dictTypeById_2 = dictTypeService.getById("2");

        System.out.println(dictTypeById_1.get().getCode());

        if (dictTypeById_2.isEmpty()){
            System.out.println("dictTypeById_2为空");
        } else {
            System.out.println(dictTypeById_2.get().getCode());
        }
    }
}

运行结果:

mock_code1
dictTypeById_2为空
  • Stubbing了dictTypeDao.findById(“1”),所以返回了我们自己预定义的值
  • 没有Stubbing dictTypeDao.findById(“2”),所以返回了空

2. 第二步:Stubbing

2.1 有返回的值的方法,模拟返回值

when(dictTypeDao.findById("1")).thenReturn(dictTypeOptional);

2.2 有返回的值的方法,模拟抛出异常

when(dictTypeDao.findById("1")).thenThrow(new RuntimeException());

2.3 无返回值void方法,模拟抛出异常

doThrow(new RuntimeException()).when(dictTypeDao).deleteById("1");

2.4 doNothing

doNothing().when(dictTypeRepository).deleteById("1");

2.5 匹配参数

上面的都是指定参数的,如果要匹配任意参数可以用anyString()、anyInt()、any(Class type)等等。

doThrow(new RuntimeException()).when(dictTypeRepository).deleteById(anyString());

3. 第三步:Verify

verify()是验证方法执行的次数

when(mockedList.get(anyInt())).thenReturn("element");
System.out.println(mockedList.get(999));
System.out.println(mockedList.get(99));

verify(mockedList, times(1)).get(anyInt());
verify(mockedList).get(anyInt());//1次的也可以简写成这样

verify(mockedList, times(2)).get(anyInt());
verify(mockedList, atMost(2)).get(anyInt());
verify(mockedList, atLeast(2)).get(anyInt());
verify(mockedList, atLeastOnce()).get(anyInt());
verify(mockedList, never()).get(9);
  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Spring Boot是一个用于构建Java应用程序的开源框架,它提供了一种简化了配置的方式来快速构建应用程序。JUnit是一个用于编写和运行单元测试的开源测试框架,而Mockito是一个用于创建和管理模拟对象的Java库。 下面是一个使用Spring Boot、JUnit和Mockito进行单元测试的示例: 假设我们有一个UserService类,它依赖于一个UserRepository接口来访问数据库并进行一些操作。我们想要对UserService的方法进行单元测试。 首先,我们需要创建一个测试类,命名为UserServiceTest。在测试类中,我们将使用JUnit的注解来标记测试方法,并使用Mockito来创建模拟对象。示例代码如下: ```java @RunWith(MockitoJUnitRunner.class) public class UserServiceTest { @InjectMocks private UserService userService; @Mock private UserRepository userRepository; @Test public void testGetUserById() { // 配置模拟对象的行为 User user = new User("1", "John"); when(userRepository.findById("1")).thenReturn(user); // 调用被测试的方法 User result = userService.getUserById("1"); // 验证结果 assertEquals("John", result.getName()); } } ``` 在上面的示例中,我们使用了@RunWith注解来指定使用MockitoJUnitRunner运行测试,这样就能自动创建和管理模拟对象。使用@InjectMocks注解将被测试的对象自动注入到测试类中,使用@Mock注解创建模拟对象。 在testGetUserById方法中,我们首先使用when方法配置userRepository模拟对象的行为,表示当传入参数为"1"时,返回一个指定的User对象。 然后,我们通过调用userService的getUserById方法来测试该方法的逻辑。最后,使用assertEquals断言来验证结果是否符合预期。 以上就是一个使用Spring Boot、JUnit和Mockito进行单元测试的示例。通过使用Mockito创建模拟对象,我们可以更容易地测试各个方法的逻辑,而不依赖于实际的数据库。这样可以提高测试效率并确保代码的质量。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瑟王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值