• Mockito verify & Junit5集成 Mockito


    Mockito 集成 Junit5

    在学习Mockito 如何集成 Junit5 之前,先来学习下 Mockito 基础的verify功能。

    Maven依赖

    本篇博客代码的Maven依赖如下,源码地址

    <dependencies>
            <dependency>
                <groupId>org.springframeworkgroupId>
                <artifactId>spring-webartifactId>
                <version>6.0.2version>
            dependency>
            <dependency>
                <groupId>org.springframeworkgroupId>
                <artifactId>spring-coreartifactId>
                <version>6.0.2version>
            dependency>
            <dependency>
                <groupId>org.springframeworkgroupId>
                <artifactId>spring-contextartifactId>
                <version>6.0.2version>
            dependency>
            
            <dependency>
                <groupId>org.apache.commonsgroupId>
                <artifactId>commons-lang3artifactId>
                <version>3.12.0version>
            dependency>
            
            <dependency>
                <groupId>org.mockitogroupId>
                <artifactId>mockito-junit-jupiterartifactId>
                <version>4.8.1version>
                <scope>testscope>
            dependency>
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <version>1.18.22version>
                <scope>compilescope>
            dependency>
            <dependency>
                <groupId>com.google.guavagroupId>
                <artifactId>guavaartifactId>
                <version>31.1-jreversion>
            dependency>
    
            <dependency>
                <groupId>org.mockitogroupId>
                <artifactId>mockito-coreartifactId>
                <version>4.8.1version>
                <scope>testscope>
            dependency>
            <dependency>
                <groupId>org.assertjgroupId>
                <artifactId>assertj-coreartifactId>
                <version>3.22.0version>
                <scope>testscope>
            dependency>
    
        dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    Mockito Verify

    Mockito Verify 方法经常被用于检查校验对象是否发生特定行为 - 即方法. 开发者可在测试方法代码的末尾使用verify方法, 确定是否调用了指定的方法。Mockito Verify主要从以下几个方面对调用行为进行判断

    • 校验是否调用指定方法
    • 校验没有调用其他方法
    • 校验方法调用次数
    • 校验调用方法顺序
    • 校验调用方法的参数

    接下来详细学习下 Mockito Verify 相关的API

    验证简单调用

    • 验证是否调用指定方法
    @Test
        void test1(){
            List<String> list = mock(ArrayList.class);
            list.size();
            // 验证是否调用size 方法
            verify(list).size();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 验证没有调用其他方法

      @Test
          void test2(){
              List<String> list = mock(ArrayList.class);
              // 验证list 对象没发生额外调用
              verifyNoMoreInteractions(list);
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    • 验证从没调用过指定方法

      @Test
          void test3(){
              List<String> list = mock(ArrayList.class);
              list.size();
              //从未调用过clear 方法
              verify(list,never()).clear();
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

    验证调用次数

    • 验证仅调用一次

          @Test
          void test0(){
              List<String> list = mock(ArrayList.class);
              list.size();
              // times(1), only(),atLeast(1) 都能实现一次调用的验证
              //verify(list,times(1)).size();
              //verify(list,only()).size();
              verify(list,atLeast(1)).size();
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    • 验证至少调用次数

      @Test
          void test0(){
              List<String> list = mock(ArrayList.class);
              list.size();
              // 最少一次调用验证
              verify(list,atLeastOnce()).size();
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    • 验证最多调用次数

      @Test
          void test0(){
              List<String> list = mock(ArrayList.class);
              list.size();
      			//最多一次
              verify(list,atMostOnce()).size();
             // 最多10次
              verify(list,atMost(10)).size();
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9

    验证调用顺序

    @Test
        void test3(){
            List<String> list = mock(ArrayList.class);
            list.size();
            list.add("hello");
            list.clear();
            InOrder inOrder = Mockito.inOrder(list);
            inOrder.verify(list).size();
            inOrder.verify(list).add(anyString());
            inOrder.verify(list).clear();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    验证调用参数

    • 验证指定参数

          @Test
          void test6(){
              List<String> list = mock(ArrayList.class);
              list.add("hello");
              verify(list).add("hello");
              // 以下两种 验证参数类型
              verify(list).add(anyString());
              verify(list).add(any(String.class));
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    • 验证捕获参数

       @Test
          void test7(){
              List<String> list = mock(ArrayList.class);
              list.addAll(Lists.<String> newArrayList("someElement"));
              ArgumentCaptor<List<String>> captor = ArgumentCaptor.forClass(List.class);
              verify(list).addAll(captor.capture());
      				// 捕获参数 并验证是否包含特定值
              final List<String> value = captor.getValue();
              assertThat(value).contains("someElement");
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10

    集成Junit5

    有了之前的基础后,接下来来学习下Mockito 如何集成 Junit5,了解Junit5如何对Mockito进行扩展。大致的流程:

    • 首先先创建Junit5扩展,该扩展使用@mock自动创建mock对象,模拟任何对象属性或者方法
    • 然后在Junit5测试类中使用Mockito扩展

    应用场景

    假设现在有一个项目,团队需要基于SpringBoot MVC的架构模式进行开发。由于业务足够复杂,分工细致。你被要求开发 service层的业务逻辑,Repository数据操作层由其他团队负责,双方根据约定好的接口进行协同开发。

    由于你的专业技术能力过硬,开发效率较高,因此已经完成了开发工作。但是其他团队Repository没有完成,只提供了对应的接口。现在您需要对整理流程进行集成测试,此时Mock排上用场。

    基础代码

    • User 实体类

      @Data
      @ToString
      @NoArgsConstructor
      public class User {
          private Integer id;
          private String name;
          private int age;
      
          public User(String name,int age){
              this.name = name;
              this.age  = age;
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
    • service接口

      import com.andy.spring.junit5.mockito.User;
      
      public interface UserService {
      
          User register(User user);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    • service实现类

      import com.andy.spring.junit5.mockito.User;
      import com.andy.spring.junit5.mockito.repository.UserRepository;
      import com.andy.spring.junit5.mockito.service.UserService;
      
      public class DefaultUserService implements UserService {
          private UserRepository userRepository;
      
          public DefaultUserService(UserRepository userRepository) {
              this.userRepository = userRepository;
          }
      
          @Override
          public User register(User user) {
              validate(user);
              User insertedUser = userRepository.insert(user);
              return insertedUser;
          }
      
          private void validate(User user) {
              if(user.getName() == null) {
                  throw new RuntimeException("用户名称不能为空");
              }
      
              if(userRepository.isUsernameAlreadyExists(user.getName())) {
                  throw new RuntimeException("用户名称已存在");
              }
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
    • Repository 接口定义

      import com.andy.spring.junit5.mockito.User;
      
      public interface UserRepository {
      
          User insert(User user);
          boolean isUsernameAlreadyExists(String userName);
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

    验证测试

    @ExtendWith(MockitoExtension.class)
    public class UserServiceTest {
        private UserService userService;
        @Mock
        private UserRepository userRepository;
        private User user;
    
        //每次运行测试方法都进行初始化
        @BeforeEach
        void init(){
            userService = new DefaultUserService(userRepository);
            //输入任意字符串 参数校验都返回false
            lenient().when(userRepository.isUsernameAlreadyExists(any(String.class)))
                    .thenReturn(false);
        }
    
        @Test
        void test1(){
            user = new User("kobe",40);
            when(userRepository.insert(any(User.class))).then(new Answer<User>() {
                int sequence = 1;
                @Override
                public User answer(InvocationOnMock invocation) throws Throwable {
                    //模拟插入db后,对ID主键进行赋值
                    User user = (User) invocation.getArgument(0);
                    user.setId(sequence++);
                    return user;
                }
            });
            userService = new DefaultUserService(userRepository);
            // When
            User insertedUser = userService.register(user);
            System.out.println("insertedUser : " + insertedUser);
            // Then 验证是否调用 insert 方法 参数为user
            verify(userRepository).insert(user);
            assertNotNull(user.getId());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    以上代码很容易理解,值得注意的是需要在class类上加上**@ExtendWith(MockitoExtension.class)**注解。测试截图

    img

  • 相关阅读:
    动静态库生成&&使用
    三步,金蝶K3的数据可视化了
    虚拟机中磁盘空间查询
    MySQL——执行一条select语句期间发生了什么
    推荐算法学习笔记2.1:基于深度学习的推荐算法-基于共线矩阵的深度推荐算法-NeuralCF模型
    软考-软件设计师 - 第12章 软件系统分析与设计【附补充常考知识点】
    4 redis的HyperLogLog入门&原理
    【操作系统】考研真题攻克与重点知识点剖析 - 第 3 篇:内存管理
    使用SpringBoot+Dubbo搭建微服务笔记
    设计模式之单例模式
  • 原文地址:https://blog.csdn.net/u013433591/article/details/128179461