精华内容
下载资源
问答
  • Spring 单元测试中如何进行 mock的实现
    2021-03-17 21:59:16

    我们在使用 Spring 开发项目时,都会用到依赖注入。如果程序依赖了外部系统或者不可控组件,比如依赖数据库、网络通信、文件系统等,我们在编写单元测试时,并不需要实际对外部系统进行操作,这时就要将被测试代码与外部系统进行解耦,而这种解耦方法就叫作 “mock”。所谓 “mock” 就是用一个“假”的服务代替真正的服务。

    那我们如何来 mock 服务进行单元测试呢?mock 的方式主要有两种:手动 mock 和利用单元测试框架 mock。其中,利用框架 mock 主要是为了简化代码编写。我们这里主要是介绍利用框架 mock,而手动 mock 只是简单介绍。

    手动 mock

    手动 mock 其实就是重新创建一个类继承被 mock 的服务类,并重写里面的方法。在单元测试中,利用依赖注入的方式使用 mock 的服务类替换原来的服务类。具体代码示列如下:

    /**

    * UserRepository

    *

    * @author star

    */

    @Repository

    public class UserRepository {

    /**

    * 模拟从数据库中获取用户信息,实际开发中需要连接真实的数据库

    */

    public User getUser(String name) {

    User user = new User();

    user.setName("testing");

    user.setEmail("testing@outlook.com");

    return user;

    }

    }

    /**

    * MockUserRepository

    *

    * @author star

    */

    public class MockUserRepository extends UserRepository {

    /**

    * 模拟从数据库中获取用户信息

    */

    @Override

    public User getUser(String name) {

    User user = new User();

    user.setName("mock-test-name");

    user.setEmail("mock-test-email");

    return user;

    }

    }

    // 进行单元测试

    @RunWith(SpringRunner.class)

    @SpringBootTest

    public class UserServiceManualTest {

    @Autowired

    private UserService userService;

    @Test

    public void testGetUser_Manual() {

    // 将 MockUserRepository 注入到 UserService 中

    userService.setUserRepository(new MockUserRepository());

    User user = userService.getUser("mock-test-name");

    Assert.assertEquals("mock-test-name", user.getName());

    Assert.assertEquals("mock-test-email", user.getEmail());

    }

    }

    从上面的代码中,我们可以看到手动 mock 需要编写大量的额外代码,同时被测试类也需要提供依赖注入的入口(setter 方法等)。如果被 mock 的类修改了函数名称或者功能,mock 类也要跟着修改,增加了维护成本。

    为了提高效率,减少维护成本,我们推荐使用单元测是框架进行 mock。

    利用框架 mock

    这里我们主要介绍 Mokito.mock()、@Mock、@MockBean 这三种方式的 mock。

    Mocito.mock()

    Mocito.mock() 方法允许我们创建类或接口的 mock 对象。然后,我们可以使用 mock 对象指定其方法的返回值,并验证其方法是否被调用。代码示列如下:

    @Test

    public void testGetUser_MockMethod() {

    // 模拟 UserRepository,测试时不直接操作数据库

    UserRepository mockUserRepository = Mockito.mock(UserRepository.class);

    // 将 mockUserRepository 注入到 UserService 类中

    userService.setUserRepository(mockUserRepository);

    User mockUser = mockUser();

    Mockito.when(mockUserRepository.getUser(mockUser.getName()))

    .thenReturn(mockUser);

    User user = userService.getUser(mockUser.getName());

    Assert.assertEquals(mockUser.getName(), user.getName());

    Assert.assertEquals(mockUser.getEmail(), user.getEmail());

    // 验证 mockUserRepository.getUser() 方法是否执行

    Mockito.verify(mockUserRepository).getUser(mockUser.getName());

    }

    @Mock

    @Mock 是 Mockito.mock() 方法的简写。同样,我们应该只在测试类中使用它。与 Mockito.mock() 方法不同的是,我们需要在测试期间启用 Mockito 注解才能使用 @Mock 注解。

    我们可以调用 MockitoAnnotations.initMocks(this) 静态方法来启用 Mockito 注解。为了避免测试之间的副作用,建议在每次测试执行之前先进行以下操作:

    @Before

    public void setup() {

    // 启用 Mockito 注解

    MockitoAnnotations.initMocks(this);

    }

    我们还可以使用另一种方法来启用 Mockito 注解。通过在 @RunWith() 指定 MockitoJUnitRunner 来运行测试:

    @RunWith(MockitoJUnitRunner.class)

    public class UserServiceMockTest {

    }

    下面我们来看看如何使用 @Mock 进行服务 mock。代码示列如下:

    @RunWith(SpringRunner.class)

    @SpringBootTest

    public class UserServiceMockTest {

    @Mock

    private UserRepository userRepository;

    @Autowired

    @InjectMocks

    private UserService userService;

    private User mockUser() {

    User user = new User();

    user.setName("mock-test-name");

    user.setEmail("mock-test-email");

    return user;

    }

    @Before

    public void setup() {

    // 启用 Mockito 注解

    MockitoAnnotations.initMocks(this);

    }

    @Test

    public void testGetUser_MockAnnotation() {

    User mockUser = mockUser();

    Mockito.when(userRepository.getUser(mockUser.getName()))

    .thenReturn(mockUser);

    User user = userService.getUser(mockUser.getName());

    Assert.assertEquals(mockUser.getName(), user.getName());

    Assert.assertEquals(mockUser.getEmail(), user.getEmail());

    // 验证 mockUserRepository.getUser() 方法是否执行

    Mockito.verify(userRepository).getUser(mockUser.getName());

    }

    }

    Mockito 的 @InjectMocks 注解作用是将 @Mock 所修饰的 mock 对象注入到指定类中替换原有的对象。

    @MockBean

    @MockBean 是 Spring Boot 中的注解。我们可以使用 @MockBean 将 mock 对象添加到 Spring 应用程序上下文中。该 mock 对象将替换应用程序上下文中任何现有的相同类型的 bean。如果应用程序上下文中没有相同类型的 bean,它将使用 mock 的对象作为 bean 添加到上下文中。

    @MockBean 在需要 mock 特定 bean(例如外部服务)的集成测试中很有用。

    要使用 @MockBean 注解,我们必须在 @RunWith() 中指定 SpringRunner 来运行测试。代码示列如下:

    @RunWith(SpringRunner.class)

    @SpringBootTest

    public class UserServiceMockBeanTest {

    @MockBean

    private UserRepository userRepository;

    private User mockUser() {

    User user = new User();

    user.setName("mock-test-name");

    user.setEmail("mock-test-email");

    return user;

    }

    @Test

    public void testGetUser_MockBean() {

    User mockUser = mockUser();

    // 模拟 UserRepository

    Mockito.when(userRepository.getUser(mockUser.getName()))

    .thenReturn(mockUser);

    // 验证结果

    User user = userRepository.getUser(mockUser.getName());

    Assert.assertEquals(mockUser.getName(), user.getName());

    Assert.assertEquals(mockUser.getEmail(), user.getEmail());

    Mockito.verify(userRepository).getUser(mockUser.getName());

    }

    }

    这里需要注意的是,Spring test 默认会重用 bean。如果 A 测试使用 mock 对象进行测试,而 B 测试使用原有的相同类型对象进行测试,B 测试在 A 测试之后运行,那么 B 测试拿到的对象是 mock 的对象。一般这种情况是不期望的,所以需要用 @DirtiesContext 修饰上面的测试避免这个问题。

    最后,小伙伴们可以在 GitHub 中获取源码。

    到此这篇关于Spring 单元测试中如何进行 mock的实现的文章就介绍到这了,更多相关Spring 单元测试mock内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    更多相关内容
  • main.java功能介绍1. 接受多层pojo递归嵌套2. 接受正则表达式(支持基本类型/String/Date类型)3. 时间函数4. 容器随机5. 基本数据类型检测方法6. 支持 以@为前缀的语义转换7. 优先级8. 1.1.0 新增内容继承类使用内部...


    所有规则均参照 前端界比较流行的mock框架:
    javascript Mock规则官网

    背景

    市面上已经有很多常用的mock框架,比如easy-mock,mockio, 等,但是生成的数据要不都是给前端使用的,要不就是在java层无法自定义扩展功能。
    但是有个前辈已经做了比较好的项目(Mock.java,jmockdata),mockj。调查了很多框架,没有一款以Java注解方式,通过简单灵活的方式快速生成想要的数。

    而且支持注解的不能很好地在注解上表达数据的关联,不支持注解的使用起来代码又过于冗余。并不能用于提供其他方面的学习,更有甚者,在比如数据开发/数仓领域为了测试代码逻辑问题,也会要求生成相关的数据用于检验代码的合理性。

    因此,本项目的宗旨是以简单,易用(使用注解),方便的方式用于代码的自动生成。

    项目目标

    通过注解的方式,通过自定义规则生成指定想要的随机测试的数据。对于很多项目开发而言,测试数据能够以最快的速度进行项目的交付。同时通过测试数据,也能够很好地进行一些框架的快速学习和教育。因此,本项目的宗旨是,以最小的成本和灵活的配置来搭建一个快速测试的数据。

    maven项目坐标

    <dependency>
      <groupId>com.github.a524631266</groupId>
      <artifactId>jmock-core</artifactId>
      <version>1.1.1</version>
    </dependency>
    

    项目依赖

    在核心业务逻辑不依赖于其他框架。不过本项目为了快速开发依赖了如下

    1. lombok 一款自动生成getter/setter/construct的工具。节约开发时间(不参与核心)
    2. log4j 日志框架,只是用来打印日志(不参与核心)
    3. findbugs google 开发的一款注解系统,这个只是在方法上加入注解,并没有做其他方面的作用(不参与核心)

    how to use

    生成一个pojo类。

    如下,首先定义了一个父类pojo.可以支持java基本类型(包装类)、字符串、数组、list,日期类型

    package com.zhangll.flink;
    
    import com.zhangll.flink.annotation.BasicTokenInfo;
    import lombok.Getter;
    import lombok.ToString;
    
    import java.util.*;
    
    @Getter
    @ToString
    public class Father {
    
        @BasicTokenInfo(min = "10", max = "100")
        private int age;
        @BasicTokenInfo(min = "100", max = "1000")
        private Integer id;
        @BasicTokenInfo(min = "1000000", max = "10000000")
        private Long money;
        @BasicTokenInfo(min = "3", max = "5")
        private String name;
    
        @BasicTokenInfo(count = "1", value = {"@First @Middle @last"})
        private String firstName;
    
        @BasicTokenInfo(value = {"张三", "李四" ,"王五" , "@First @Middle @last"}, count = "1")
        private String innerName;
    
        @BasicTokenInfo(min = "10", max = "20")
        private String Address;
        @BasicTokenInfo(min = "1000", max = "2000", dmin = "3", dmax = "8")
        private double money_d;
        @BasicTokenInfo(min = "1000", max = "2000", dmin = "3", dmax = "8")
        private float money_f;
        private char char_1;
        private Character a = 'c';
        private Short wShort;
    
        @BasicTokenInfo(min = "1000", max = "2000")
        private short uShort;
    
        private java.sql.Date date;
        private java.sql.Time time;
        private java.sql.Timestamp timestamp;
        // 男为1 女为0
        private boolean sex;
        @ContainerTokenInfo(
                innerBasicType = @BasicTokenInfo(min = "4", max = "7")
        )
        private ArrayList<String> sonsNameList;
        private List<String> sonsNameList2;
        private List<Integer> sonsAgeList;
        private LinkedList<Double> sonsMoneyList;
        private LinkedList<Long> sonsLongList;
    
        @ContainerTokenInfo(
                innerBasicType =  @BasicTokenInfo(min = "15", max = "30")
        )
        private Set<String> sonsNameSet;
    
        private Son son;
    
        private String[] stringArr;
        private Double[] doubleWrapperArr;
        private double[] doubleNoWrapperArr;
        private int[] intNoWrapperArr;
        private Integer[] intWrapperArr;
    
        private char[] charNoWrapperArr;
        private Character[] charWrapperArr;
    //
        @ContainerTokenInfo(
                innerPojoType =  @PojoTokenInfo(
                        {
                                @TokenMapping(field = "id", basicTokenInfo = @BasicTokenInfo(min = "1", max = "10"))
                        }
                ),
                innerBasicType = @BasicTokenInfo(min = "1233", max = "12324")
        )
        @BasicTokenInfo(min = "1", max = "2")
        private ArrayList<Son> sonslist;
        private Son[] sonlist2;
        // 正则表达式生成
        @BasicTokenInfo(value = {"/\\d{ 1, 3}  abcd\\/ \\d/ [a-bA-H1-4]{1,5}/"})
        private String regrex;
        private Date date2;
    }
    
    
    @ToString
    public class Son {
        private String name;
        private int id;
        @FieldTokenType(min = "10", max = "20")
        private int age;
        
        private SonSon son;
    }
    
    

    main.java

    使用方法 context 为mock的上下文,可以通过上下文的mock方法

    AnnotationMockContext context = new AnnotationMockContext();
    for (int i = 0; i < 2; i++) {
        Object mock = context.mock(Father.class);
        System.out.println(mock);
    }
    

    输出结果为

    1. Father(age=97, id=749, money=7012716, name=令, firstName=夏 幸 祁, innerName=张三, Address=摇, money_d=1474.3962, money_f=1113.0878, char_1=Ȗ, a=ʂ, wShort=203, uShort=1717, date=2020-10-31, time=07:25:40, timestamp=2020-10-30 02:06:41.0, sex=false, sonsNameList=[箭, 专, 谦, 舌, 轮, 喷, 螺, 塔, 鲜, 避], sonsNameList2=[唯, 再, 杯, 锡, 燃, 宵, 匪, 三, 畜, 向], sonsAgeList=[517, 846, 653, 263, 667, 136, 867, 56, 378, 182], sonsMoneyList=[7.4, 2.2, 5.957, 9.9405, 8.41, 1.51, 1.89, 8.3, 7.774, 3.4809], sonsLongList=[287, 998, 743, 286, 737, 750, 388, 793, 273, 865], sonsNameSet=[着, 乔, 泄, 否, 仙, 循, 绍, 中, 蛾, 环], son=Son(name=浮, id=715, age=17, son=SonSon(name=伏, id=362, age=18)), stringArr=[拒, 瓣, 他, 烘, 熔, 竟, 据, 桌, 党, 化], doubleWrapperArr=[5.15, 3.323, 2.3334, 7.8, 3.4, 5.64, 3.805, 8.64, 2.808, 7.8388], doubleNoWrapperArr=[6.2, 7.04, 3.414, 4.79, 4.5917, 4.518, 1.0301, 2.82, 8.421, 8.361], intNoWrapperArr=[686, 621, 416, 538, 581, 253, 383, 845, 533, 522], intWrapperArr=[844, 20, 161, 981, 279, 682, 607, 707, 8, 111], charNoWrapperArr=[˛, Ƅ, Œ, ŕ, ̊, ͛, Ü, ĉ, ȍ, Ǫ], charWrapperArr=[q, ǃ, ȳ, ̗, ̗, Ϊ, ɭ, Z, Ȇ, ¶])
    
    2. Father(age=96, id=616, money=7651289, name=倒, firstName=汪 暨 国, innerName=张三, Address=吗, money_d=1669.9100904, money_f=1670.301, char_1=Ə, a=˴, wShort=103, uShort=1942, date=2020-10-29, time=09:53:48, timestamp=2020-10-30 09:56:16.0, sex=true, sonsNameList=[夺, 画, 刑, 袋, 对, 端, 舌, 膨, 掩, 妄], sonsNameList2=[贵, 蛇, 罢, 剃, 另, 扯, 延, 削, 股, 穴], sonsAgeList=[377, 688, 492, 833, 572, 679, 261, 162, 743, 816], sonsMoneyList=[8.974, 2.8, 3.1843, 3.7751, 4.6, 3.069, 9.51, 8.473, 1.5364, 3.28], sonsLongList=[261, 604, 445, 731, 400, 593, 618, 627, 956, 146], sonsNameSet=[言, 攀, 窃, 米, 炉, 蕉, 孩, 配, 苏, 迟], son=Son(name=首, id=870, age=18, son=SonSon(name=剖, id=144, age=15)), stringArr=[型, 渠, 低, 胆, 杨, 魔, 桃, 扒, 晨, 因], doubleWrapperArr=[7.173, 3.519, 9.9, 3.018, 3.79, 8.825, 3.7, 6.542, 5.051, 5.8], doubleNoWrapperArr=[5.21, 8.61, 4.8, 8.813, 4.5921, 2.91, 6.5, 6.7, 7.361, 3.0755], intNoWrapperArr=[273, 61, 734, 572, 772, 871, 932, 266, 832, 625], intWrapperArr=[34, 205, 401, 223, 297, 21, 605, 945, 140, 812], charNoWrapperArr=[΃, Ü, ĭ, ͆, ʹ, ƥ, Ζ, ̂, c, ɸ], charWrapperArr=[Τ, Ǵ, ρ, Ȗ, Ɠ, ΀, ċ, ʘ, ², ͊])
    

    功能介绍

    案例地址

    1. 接受多层pojo递归嵌套

    @ToString
    class Father{
        Son son;
    }
    @ToString
    class Son {
        Son2 son2;
        private Date date;
    }
    @ToString
    class Son2 {
        @BasicTokenInfo(min = "1" , max = "100")
        private int a;
    }
    
    
    ...main(){
       AnnotationMockContext annotationMockContext = new AnnotationMockContext();
        for (int i = 0; i < 100; i++) {
            Object mock = annotationMockContext.mock(Father.class);
            System.out.println(mock);
        }
    }
    
    

    2. 接受正则表达式(支持基本类型/String/Date类型)

    添加正则文法匹配功能 (has done)[使用Java原生的Regrex表达式]

    input

     @BasicTokenInfo(value = {"/\\d{ 1, 3}  abcd\\/ \\d/ [a-bA-H1-4]{1,5}/" , "/[a-z][A-Z][0-9]/", "/\\w\\W\\s\\S\\d\\D/", "/\\d{5,10}/"})
        private String regrex;
        
        
    @BasicTokenInfo(value = "/\\d{1,3}/")
        private int regrexInteger;
    @BasicTokenInfo(value = "/\\d{1,3}\\.\\d{1,6}/")
    private double regrexDouble;
    @BasicTokenInfo(value = "/201[1-8]-0[1-8]-0[1-8]/")
        private Date dateRegrex;
    
    @BasicTokenInfo(value = "/[a-zA-Z]/")
        private Character charRegrex;
    

    result 会选取其中一个作为regrex

    RegrexPojo(regrex=42283, regrexInteger=656, regrexDouble=476.8, dateRegrex=2014-04-03, charRegrex=n)
    RegrexPojo(regrex=2028284986, regrexInteger=20, regrexDouble=27.74132, dateRegrex=2015-01-01, charRegrex=e)
    RegrexPojo(regrex=1  abcd/ 4/ 4hb, regrexInteger=393, regrexDouble=334.27, dateRegrex=2013-03-02, charRegrex=w)
    

    3. 时间函数

    @ToString
    class DatePojo{
    // 表示最小2010-10-10日,最大2010-10-20
        @BasicTokenInfo(min = "2010-10-10" , max = "2010-10-20")
        Date date;
    
    // 同上
     @BasicTokenInfo(min = "2010-10-10 00:10:20" , max = "2010-10-20 00:10:20")
        Timestamp timestamp;
    
        // 同上
        @BasicTokenInfo(min = "00:10:20" , max = "00:11:20")
        Time time;
    
    // 步长为 2天
        @BasicTokenInfo(min = "2010-10-10" , max = "2010-10-20",step = "2")
        Date date2;
    
        // 步长为2秒
        @BasicTokenInfo(min = "2010-10-10 00:10:20" , max = "2010-10-20 00:10:20", step = "2")
        Timestamp timestamp2;
    
        // 步长为2秒
        @BasicTokenInfo(min = "00:10:20" , max = "00:11:20", step = "2")
        Time time2;
    }
    

    out put

    DatePojo(date=2010-10-11, timestamp=2010-10-10 17:30:22.0, time=00:10:27, date2=2010-10-10, timestamp2=2010-10-10 00:10:20.0, time2=00:10:20)
    DatePojo(date=2010-10-10, timestamp=2010-10-17 20:55:40.0, time=00:11:16, date2=2010-10-12, timestamp2=2010-10-10 00:10:22.0, time2=00:10:22)
    DatePojo(date=2010-10-10, timestamp=2010-10-19 14:30:19.0, time=00:10:52, date2=2010-10-14, timestamp2=2010-10-10 00:10:24.0, time2=00:10:24)
    DatePojo(date=2010-10-17, timestamp=2010-10-12 22:59:23.0, time=00:10:35, date2=2010-10-16, timestamp2=2010-10-10 00:10:26.0, time2=00:10:26)
    ....
    

    4. 容器随机

    class Array01 {
        //容器大小在2和3数量之间,其中内的基本类型的约束在
        @ContainerTokenInfo(innerBasicType = @BasicTokenInfo(min = "2", max = "3"))
        @BasicTokenInfo(min = "2", max = "3")
        List<String> list;
        @ContainerTokenInfo(innerBasicType = @BasicTokenInfo(min = "1", max = "10"))
        @BasicTokenInfo(min = "2", max = "3")
        ArrayList<Integer> arrayList;
        @ContainerTokenInfo(innerBasicType = @BasicTokenInfo(min = "1", max = "10"))
        @BasicTokenInfo(min = "2", max = "3")
        Set<Boolean> set;
        @ContainerTokenInfo(innerBasicType = @BasicTokenInfo(min = "1", max = "10"))
        @BasicTokenInfo(min = "2", max = "3")
        HashSet<Double> hashSet;
        @ContainerTokenInfo(innerBasicType = @BasicTokenInfo(min = "1", max = "10"))
        @BasicTokenInfo(min = "2", max = "3")
        LinkedList<Short> linkedList;
    
    }
    

    OutPut

    Array01(list=[拿晚芝, 摩笛宋, 贪会信], arrayList=[1, 5], set=[true], hashSet=[6.1], linkedList=[7, 6, 2])
    Array01(list=[键僻旋, 便培, 梨忙买], arrayList=[4, 10], set=[true], hashSet=[5.8, 3.1, 1.9], linkedList=[6, 8, 3])
    Array01(list=[亏二馅, 落键滴], arrayList=[10, 8, 8], set=[true], hashSet=[9.3, 2.1], linkedList=[5, 8])
    Array01(list=[降孕待, 虾蹲], arrayList=[4, 5, 6], set=[true], hashSet=[7.1, 8.8], linkedList=[10, 10, 6])
    Array01(list=[眠岭, 宁酒], arrayList=[3, 6, 7], set=[false, true], hashSet=[8.6, 3.1], linkedList=[7, 4])
    

    5. 基本数据类型检测方法

    以int为例子

    @ToString
    class IntegerPojo{
        @BasicTokenInfo(min = "1000", max = "10000")
        private Integer int1;
    
        @BasicTokenInfo(value = {"1" , "10", "100"})
        private int int2;
    }
    
    1. int1 表示为 最小为1000, 最大 10000的随机值
    2. int2 表示为 从 value中 1, 10 , 100 为随机值任一取一个值

    同理其他基本数据类型. 不过比较特殊的是boolean类型

    @BasicTokenInfo(min = "1" , max = "99")
    private boolean bool3;
    

    这里min和max指的是 boolean为true 的概率为 max/ (max + min) = 99%

    6. 支持 以@为前缀的语义转换

    比如 @First表示姓名 @Middle

     "@First @Middle @last"
    

    7. 优先级

    在pojo中可能会嵌套pojo,同时我们定义

    class ListPojo{
     @ContainerTokenInfo(
                innerPojoType = @PojoTokenInfo(
                value = {
                        @TokenMapping(field = "bool3",
                                basicTokenInfo = @BasicTokenInfo(min = "1", max = "2"))
                })
        )
        @BasicTokenInfo(min ="1", max = "1")
        BooleanPojo[] booleanPojos;
    }
    
    class BooleanPojo{
        @BasicTokenInfo(min = "1" , max = "100")
        private boolean bool3;
    }
    

    可以想象,在容器类中定义的优先级是高于原始类的

    因此 @BasicTokenInfo(min = “1”, max = “2”) 会覆盖 @BasicTokenInfo(min = “1” , max = “100”)规则

    8. 1.1.0 新增内容

    继承类使用

    demo

    内部类使用

    demo innerClass

    详细案例

    请点击此处,欢迎start项目,请作者喝杯茶哈

    框架支持的类型

    支持普通class/内部class

    后续可以添加一个

    内置基本类型@BasicTokenInfo修饰的变量类型

    分类类描述
    Java包装类Integer.class
    Java包装类Character.class
    Java包装类Double.class
    Java包装类Float.class
    Java包装类Long.class
    Java包装类Short.class
    Java包装类Boolean.class
    java基本类型int.class
    java基本类型char.class
    java基本类型double.class
    java基本类型float.class
    java基本类型long.class
    java基本类型short.class
    java基本类型boolean.class
    字符串类型String.class
    时间类型sql.Date.class
    时间类型sql.Time.class
    时间类型sql.Timestamp.class
    枚举类型Enum.class

    @ContainerTokenInfo修饰的类型

    分类类描述
    数组类型Array.class(int[]…,String[],Object[])
    集合类型List.class[List子类或list都可以]
    集合类型Set.class[Set/HashSet等等Set的子类]

    Pojo类型

    分类类描述
    Pojo类型Object.class

    注解使用方式

    注解1: @BasicTokenInfo

    用来表达每个字段内置基本类型的取值的约束条件

    注解2: @ContainerTokenInfo

    容器,(Set和List)类型的数据之间

    mockContext(上下文)对象

    mock上下文对象是用来构建mock对象的上下文.可以利用上下文,关联生成对象之间的关系

    @ToString
    class Step01 {
        @BasicTokenInfo(min = "5", max = "10", step = "2")
        private int increase;
    }
    
    main(){
        AnnotationMockContext annotationMockContext = new AnnotationMockContext();
            for (int i = 0; i < 100; i++) {
                Object mock = annotationMockContext.mock(Step01.class);
                System.out.println(mock);
            }
    }
    

    output

    Step01(increase=5)
    Step01(increase=7)
    Step01(increase=9)
    Step01(increase=5)
    Step01(increase=7)
    Step01(increase=9)
    ....
    

    此时输出的随机对象是以5为其实开始点,并以步长2在 [min, max]依次轮询增加.

    2.step功能还能在array或者list中使用

    注意: 注解的表达能力有限

    在开发的过程中,注解无法嵌套定义,会出现 cyclic annotation element type 编译错误

    即,在使用注解的时候,一般表达的是一层含义,无法嵌套使用

    不过在平时使用的时候,一般都是比较简单的pojo类来定义,因此也不会出现嵌套内嵌套的关系。

    本项目的目的是基于满足最基本所需。

    为什么不用map?

    本人认为 map对象一般是可以直接转化为pojo的key value表达式就可以表达,按照语义上来说map数据结构是pojo的特殊表达方式。

    如果大家有疑惑,可以发起issue,我会及时解答的。希望能够帮你在前进的路上减轻一些负担。

    1.1.2 新增内容

    1. Enum 类型的随机生成
      查看地址

    2. 开放支持用户自定义规则,详情请看

    自定义Enum扩展类

    注册方式

    后续进展

    会支持更多@前缀语义

    有任何需求的同学可以参与进去哦,也欢迎提bug和随机需求

    展开全文
  • Java测试工具Mock详解

    千次阅读 2021-11-22 10:21:49
    初识 Mockito这个测试框架后,我们要使用 Mock的属性创建一个被测试类实例时,大概会下面这么纯手工来打造。 假定类UserService有一个属性UserDao userDao,需要构造UserService实例时 Mock内部状态 UserDao ...

    使用 Mockito 的 @InjectMocks 创建被测试类实例

    初识 Mockito 这个测试框架后,我们要使用 Mock 的属性创建一个被测试类实例时,大概会下面这么纯手工来打造。

    假定类 UserService 有一个属性 UserDao userDao, 需要构造 UserService 实例时 Mock 内部状态

    UserDao userDao = Mockito.mock(UserDao.class);
    UserService testMe = new UserService(userDao);

    如此,userDao 的行为就可以自由模拟了,这种纯手工方式都不需要给测试类添加

    @RunWith(MockitoJunitRuner.class)
    //或
    MockitoAnnotations.initMocks(this);

    因为上面两句是给 Mockito 的注解使用的。

    如果所有的 Mock 对象全部通过手工来创建,那就不容易体现出 Mockito 的优越性出来。因此对于被测试对象的创建,Mock 属性的注入应该让 @Mock 和 @InjectMocks 这两个注解大显身手了。

    标注在实例变量上的 @Mock 相当于是 Mockito.mock(Class) 创建了一个 Mock 对象,而 @InjectMock 标的实例会寻找到相应 Mock 属性想法构造出被测试类的实例。看下面的例子:

    UserService 类

    public class UserService {
     
    
        private UserDao userDao;
    
     
    
        public UserService(UserDao userDao) {
            System.out.println("Constructor called");
    
            this.userDao = userDao;
    
        }
    
     
    
        public UserDao getUserDao() {
            return userDao;
    
        }
    
    }

    UserServiceTest 类

    @RunWith(MockitoJUnitRunner.class)
    
    public class UserServiceTest {
     
    
        @Mock
    
        private UserDao userDao;
    
    
        @InjectMocks
    
        private UserService testMe;
    
     
        @Test
    
        public void testInjectMocks() {
            System.out.println(testMe.getUserDao().getClass());
    
        }
    
    }


    Constructor called上面测试用例的输出为

    class cc.unmi.UserDao$MockitoMock$878185941

    证明了 Mock 对象 userDao 成功的通过构造函数注入了 testMe 实例。

    除了通过构造函数注入 Mock 的属性外, @InjectMocks  还能通过 setter 方法,属性注入。私有的构造函数,setter 方法,属性都无法阻止 @InjectMocks 注入 Mock 对象。

    下面是理解自 Mockito 官方对 @InjectMocks 的 JavaDoc 说明,链接:InjectMocks - mockito-core 2.13.0 javadoc

    1. Mockito 尝试按 非默认构造函数setter 方法属性 的顺序来注入 Mock 对象。如果存在一个有参数的构造函数,那么 setter 方法 和 属性  注入都不会发生。也就是说 非默认构造函数 不会与后两种方式同时发生,但找不到 setter 注入的 Mock 对象还会尝试用 属性 来直接注入。
    2. 如果 @InjectMocks 对象只有默认构造数,那么会调用该默认构造函数,并且依次采用下面两种方式注入属性。
    3. 非默认构造函数注入: Mockito 会选择参数个数最多的构造函数(称之为最大构造函数) -- 这样可以尽可能注入多的属性。但是有多个最大构造函数,Mockito 究竟选择哪一个就混乱,测试时应该避免这种情况的发生。
    4. 如果构造函数中含有不可 Mock 的参数(基本类型), 则该构造函数将被 @InjectMocks 忽略掉。
    5. setter 方法注入: 和 Spring 类似,Mockito 首先根据属性类型(或擦除类型)找到 Mock 对象。存在多个相同类型 Mock 对象则按名称(@Mock(name="userDao1"))进行匹配,默认名称为空。不能按名称匹配到的话,可能会选择最后声明的那个,不确定性。
    6. 属性 注入: 按 Mock 对象的类型或是名称的匹配规则与 setter 方法注入 是一样的。

    现在来开始有事实验证上面理解的 @InjectMocks 理论:

    调用最大构造函数,调用了非默认构造函数将不会采用 setter 方法 和 属性 注入

    public class UserService {
        public UserDao userDao;
    
     
    
        private UserService(String s1) {
            System.out.println("Constructor 1 called");
    
        }
    
     
    
        private UserService(String s1, String s2) {
            System.out.println("Constructor 2 called");
    
        }
    
     
    
        public void setUserDao(UserDao userDao) {
            System.out.println("call setter");
    
            this.userDao = userDao;
    
        }
    
    }
    
     
    
    @RunWith(MockitoJUnitRunner.class)
    
    public class UserServiceTest {
        
    
        @Mock
    
        private UserDao userDao;
    
     
    
        @InjectMocks
    
        private UserService testMe;
    
     
    
        @Test
    
        public void testInjectMocks() {
            System.out.println(testMe.userDao);
    
        }
    
    }

    上面测试执行输出为:

    Constructor 2 called
    null

    同时证明了私有的构造函数一样被调用。

    @InjectMocks 调用了默认构造函数后还能同时应用 setter 方法 和 属性 注入两种式

    public class UserService {
    
    
        public UserDao userDao;
    
        private BookDao bookDao;
    
    
    
        public UserService() {
    
            System.out.println("Constructor 0 called");
    
        }
    
    
    
        private void setUserDao(UserDao userDao) {
    
            System.out.println("call setter");
    
            this.userDao = userDao;
    
        }
    
    
        public BookDao getBookDao() {
    
            return this.bookDao;
    
        }
    
    }
    
    
    
    @RunWith(MockitoJUnitRunner.class)
    
    public class UserServiceTest {
    
    
        @Mock
        private UserDao userDao;
    
    
        @Mock
        private BookDao bookDao;
    
    
    
        @InjectMocks
    
        private UserService testMe;
    
    
        @Test
        public void testInjectMocks() {
    
            System.out.println(testMe.userDao.getClass());
    
            System.out.println(testMe.getBookDao().getClass());
    
        }
    
    }

    测试代码输出如下:

    Constructor 0 called
    class cc.unmi.UserDao$MockitoMock$1978393893
    class cc.unmi.BookDao$MockitoMock$910006861

    默认构造函数调用了,userDao 通过  setter 方法注入的,bookDao 通过属性直接注入的。把 setUserDao(..) 方法和 bookDao  设置为私有也是为了证明可见性不是障碍,当然 public 的更不是事。

    含有基本类型参数的构造函数将被 @InjectMocks 忽略掉

    public class UserService {
    
    
        public UserDao userDao;
    
    
        public UserService() {
    
            System.out.println("Constructor 0 called");
    
        }
    
    
        private UserService(UserDao userDao, boolean flag) {
    
            System.out.println("Constructor 2 called");
    
        }
    
    }
    
    
    
    @RunWith(MockitoJUnitRunner.class)
    
    public class UserServiceTest {
    
    
        @Mock
    
        private UserDao userDao;
    
    
        @InjectMocks
    
        private UserService testMe;
    
    
        @Test
    
        public void testInjectMocks() {
    
            System.out.println(testMe.userDao.getClass());
    
        }
    
    }

    执行测试用例的输出为:

    Constructor 0 called
    class cc.unmi.UserDao$MockitoMock$286493746

    由于无法构造出 Mock 的 boolean 类型,所以 UserService(UserDao userDao, boolean flag) 被忽略,调用了默认构造函数,并且 userDao 通过属性进行了注入。

    多个相同类型的 Mock 对象通过名称进行匹配

    public class UserService {
    
        public UserDao userDao2;
    
        private UserService(UserDao userDao1, String abc) {
    
            System.out.println("Constructor 2 called");
    
            this.userDao2 = userDao1;
    
        }
    }
    
    
    @RunWith(MockitoJUnitRunner.class)
    public class UserServiceTest {
    
    
        @Mock(name = "userDao1")
        private UserDao userDao1;
    
    
        @Mock(name = "userDao2")
        private UserDao userDao2;
    
    
        @InjectMocks
        private UserService testMe;
    
    
        @Test
    
        public void testInjectMocks() {
    
            Assert.assertEquals(userDao1, testMe.userDao2);
    
        }
    
    }


    输出为:

    Constructor 2 called

    UserService 类中对 userDao2 和 userDao1 名称进行错位安排是为了证明名称匹配是根据注入点处的名称对比的。例如

    1. 构造函数注入,根据参数名进行匹配
    2. setter 方法注入,根据 setter 方法名, 如 setUserDao1(..), 或 setUserDao2(..) 匹配的,与方法参数无关
    3. 属性注入自然是以属性名本身为准

    同时该例也证明了构造函数 UserService(UserDao userDao1, String abc) 对 @InjectMocks 是可见的,因为 String 是非基本类型,也是可以 Mock String 类型的。

    因此,需要我们留意的是,产品代码构造函数的变动可能会改变测试代码的行为,或是导致测试的失败。

    @InjectMocks 只能注入 Mock 对象,例如以下均是 Mock 对象

    1. UserDao userDao = Mockito.mock(UserDao.class);
    2. @Mock private UserDao userDao;
    3. @Mock private UserDao userDao = new UserDao();    //Mockito 将会对 userDao 重新赋值为一个  Mock 对象
    4. UserDao userDao = spy(new UserDao());

    如果是一个普通对象,例如下面的声明

    private UserDao userDao = new UserDao();
    
    @InjectMocksprivate UserService testMe;


    @InjectMocks 如何费尽心思都无法把这个  userDao  注入到 testMe  测试对象中去的。对它 spy 一下就可以被注入了。

    @Mock 和 @InjectMocks 会把自己赋的值丢弃

    前面提到 @Mock private UserDao userDao = new UserDao(); 最终的 userDao 是一个 Mock  对象,@InjectMocks  也一样

    @InjectMocks
    
    private UserService testMe = new UserService(); 

    虽然会调用一下 new UserService() 创建一个对象,但最终的值是由 @InjectMocks 产生的。


    备注一个使用 @Mock 对象创建被测试实例的错误

    @RunWith(MockitoJUnitRunner.class)
    public class UserServiceTest {
    
    
        @Mock
        private UserDao userDao;
    
    
        private UserService testMe = new UserService(userDao); //此时 userDao 还是 null
    
    
    
        @Before
        public void setup() {
    
            testMe = new UserService(userDao); //这里的 userDao 才是一个 Mock 对象
    
        }
    
    }

    静态测试类的示例

    @RunWith(PowerMockRunner.class)
    @PrepareForTest({
            SpringContext.class,KeywordRuleCacheData.class
    })
    public class KeywordRuleCacheDataRefreshDealTest {
    
        @InjectMocks
        KeywordRuleCacheDataRefreshDeal keywordRuleCacheDataRefreshDeal;
    
        @Test
        public void run() throws BaseAppException {
            PowerMockito.mockStatic(SpringContext.class);
            PowerMockito.mockStatic(KeywordRuleCacheData.class);
            PowerMockito.when(KeywordRuleCacheData.refushKeywordRuleCacheData()).thenReturn(true);
            keywordRuleCacheDataRefreshDeal.run();
        }
    
    }

    展开全文
  • 使用mock进行java单元测试

    千次阅读 2017-12-15 16:43:39
    Java单元测试对于开发人员质量保证至关重要,尤其当面对一团乱码的遗留代码时,没有高覆盖率的单元测试做保障,没人敢轻易对代码进行重构。然而单元测试的编写也不是一件容易的事情,除非使用TDD方式,否则编写出...

    Java单元测试对于开发人员质量保证至关重要,尤其当面对一团乱码的遗留代码时,没有高覆盖率的单元测试做保障,没人敢轻易对代码进行重构。然而单元测试的编写也不是一件容易的事情,除非使用TDD方式,否则编写出容易测试的代码不但对开发人员的设计编码要求很高,而且代码中的各种依赖也常常为单元测试带来无穷无尽的障碍。
    令人欣慰的是开源社区各种优秀的Mock框架让单元测试不再复杂,本文简单介绍EasyMock,PowerMock等的基本常用用法。
    Mock说白了就是打桩(Stub)或则模拟,当你调用一个不好在测试中创建的对象时,Mock框架为你模拟一个和真实对象类似的替身来完成相应的行为。
    EasyMock:
    使用如下方式在Maven中添加EasyMock的依赖:

    org.easymock
    easymock
    3.2
    test

    EasyMock使用动态代理实现模拟对象创建,其基本步骤为以下四步:
    以数据库应用为例的被测试代码如下:
    public class UserServiceImpl{
    private UserDao dao;
    public User query(String id) throws Exception{
    try{
    return dao.getById(id);
    }catch(Exception e){
    throw e;
    }
    return null;
    }
    }

    public class UserDao{
    public User getById(String id) throws Exception{
    try{
    return ……;
    }catch(Exception e){
    throw e;
    }
    return null;
    }
    }
    现在希望对UserServiceImpl进行测试,而UserDao开发组只给出接口,尚未完成功能实现。
    使用Mock对UserDao进行模拟来测试UserServiceImpl。
    (1).基本的测试代码如下:
    public class UserServiceImplTest {
    @Test
    public void testQuery() {
    User expectedUser = new User();
    user.setId(“1001”);
    UserDao mock = EasyMock.createMock(UserDao.class);//创建Mock对象
    Easymock.expect(mock.getById(“1001”)).andReturn(expectedUser);//录制Mock对象预期行为
    Easymock.replay(mock);//重放Mock对象,测试时以录制的对象预期行为代替真实对象的行为

            UserServiceImpl  service = new UserServiceImpl();
            service.setUserDao(mock);
            user user = service.query("1001");//调用测试方法
            assertEquals(expectedUser, user); //断言测试结果 
            Easymock.verify(mock);//验证Mock对象被调用
        }
    } 
    

    注意:
    在EasyMock3.0之前,org.easymock.EasyMock使用JDK的动态代理实现Mock对象创建,因此只能针对接口进行Mock,org.easymock.classextension.EasyMock使用CGLIB动态代理创建Mock对象,可以针对普通类进行Mock。
    在EasyMock3.0之后,org.easymock.classextension.EasyMock被废弃,使用org.easymock.EasyMock可以针对接口和普通类进行Mock对象创建。
    (2).调用测试设定:
    如果想测试UserServiceImpl调用了UserDao的getById方法3次,则使用如下代码即可:
    Easymock.expect(mock.getById(“1001”)).andReturn(exceptUser).times(3);
    (3).方法异常:
    如果想测试UserServiceImpl在调用UserDao的getById方法时发生异常,可以使用如下代码:
    Easymock.expect(mock.getById(“1001”)).andThrow(new RuntimeException());
    在测试UserServiceImpl时就可以使用try-catch捕获Mock的异常。
    (4).基本参数匹配:
    上面的方法在Mock UserDao的getById方法时传入了“0001”的预期值,这种方式是精确参数匹配,如果UserServiceImpl在调用是传入的参数不是“0001”就会发生Unexpect method的Mock异常,可以使用下面的方法在Mock时进行参数匹配:
    Easymock.expect(mock.getById(Easymock.isA(String.class))).andReturn(exceptedUser).times(3);
    isA()方法会使用instanceof进行参数类型匹配,类似的方法还有anyInt(),anyObject(), isNull(),same(), startsWith()……
    (5).数组类型参数匹配:
    如果UserServiceImpl在调用UserDao的方法时传入的参数是数组,代码如下:
    public class UserServiceImpl{
    private UserDao dao;
    public List queryNames(String[] ids) throws Exception{
    try{
    return dao.getNames(ids);
    }catch(Exception e){
    throw e;
    }
    return null;
    }
    }

    public class UserDao{
    public List getNames(String[] ids) throws Exception{
    try{
    return ……;
    }catch(Exception e){
    throw e;
    }
    return null;
    }
    }
    此时有两种办法来进行参数匹配:
    a.数组必须和测试给定的一致:
    Easymock.expect(mock.getNames(EasyMock.aryEq(testIds))).andReturn(exceptedNames);
    b.不考虑测试数组内容:
    Easymock.expect(mock.getNames(EasyMock.isA(String[].class))).andReturn(exceptedNames);
    (6).void方法Mock:
    如果要Mock的方法是无返回值类型,例子如下:
    public class UserDao {
    public void updateUserById(String id) throws Exception{
    try{
    update…
    }catch(Exception e){
    throw e;
    }
    }
    }
    a.正常Mock代码如下:
    mock.updateUserById(“TestId”);
    EasyMock.expectLastCall().anytimes();
    b.模拟发生异常的Mock代码如下:
    mock.updateUserById(“TestId”);
    EasyMock.expectLastCall().andThrow(new RuntimeException()).anytimes();
    (7).多次调用返回不同值的Mock:
    对于迭代器类型的遍历代码来说,需要在不同调用时间返回不同的结果,以JDBC结果集为例代码如下:
    public List getUserNames () throws Exception{
    List usernames = new ArrayList();
    ResultSet rs = pstmt.executeQuery(query);
    try {
    while(rs.next()){
    usernames.add(rs.getString(2));
    }
    } catch (SQLException e) {
    throw e;
    }
    }
    在Mock结果集的next方法时如果总返回true,则代码就会陷入死循环,如果总返回false则代码逻辑根本无法执行到循环体内。
    正常的测试逻辑应该是先返回几次true执行循环体,然后在返回false退出循环,使用Mock可以方便模拟这种预期的行为,代码如下:
    EasyMock.expect(rs.next()).andReturn(true).times(2).andReturn(false).times(1);
    更多的关于EasyMock的用法,请参考EasyMock官方文档:
    http://easymock.org/EasyMock3_0_Documentation.html
    PowerMock:
    上面介绍的EasyMock可以满足单元测试中的大部分需求,但是由于动态代理是使用了面向对象的继承和多态特性,JDK自身的动态代理只针对接口进行代理,其本质是为接口生成一个实现类,而CGLIB可以针对类进行代理,其本质是将类自身作为基类。
    如果遇到了静态、final类型的类和方法,以及私有方法,EasyMock的动态代理局限性使得无法测试这些特性情况。
    PowerMock是在EasyMock基础上进行扩展(只是补充,不是替代),使用了字节码操作技术直接对生成的字节码类文件进行修改,从而可以方便对静态,final类型的类和方法进行Mock,还可以对私有方法进行Mock,更可以对类进行部分Mock。
    PowerMock的工作过程和EasyMock类似,不同之处在于需要在类层次声明@RunWith(PowerMockRunner.class)注解,以确保使用PowerMock框架引擎执行单元测试。
    通过如下方式在maven添加PowerMock相关依赖:

    org.powermock
    powermock-api-easymock
    1.5.1
    test


    org.powermock
    powermock-module-junit4
    1.5.1
    test

    例子如下:
    (1).Miock final类的静态方法:
    如果测试代码中使用到了java.lang.System类,代码如下:
    public class SystemPropertyMockDemo {
    public String getSystemProperty() throws IOException {
    return System.getProperty(“property”);
    }
    }
    如果对System.getProperty()方法进行Mock,代码如下:
    @RunWith(PowerMockRunner.class)
    @PrepareForTest({SystemPropertyMockDemo.class})//声明要Mock的类
    public class SystemPropertyMockDemoTest {
    @Test
    public void demoOfFinalSystemClassMocking() throws Exception {
    PowerMock.mockStatic(System.class);//Mock静态方法
    EasyMock.expect(System.getProperty(“property”)).andReturn(“my property”);//录制Mock对象的静态方法
    PowerMock.replayAll();//重放Mock对象
    Assert.assertEquals(“my property”,
    new SystemPropertyMockDemo().getSystemProperty());
    PowerMock.verifyAll();//验证Mock对象
    }
    }
    非final类的静态方法代码相同,注意(上述代码只能在EasyMock3.0之后版本正常运行)
    如果要在EasyMock3.0之前版本正常Mock final类的静态方法,需要使用PowerMockito,
    通过如下方式在maven中添加PowerMockito相关依赖:

    org.powermock
    powermock-api-mockito
    1.5.1
    test

    代码如下:
    @RunWith(PowerMockRunner.class)
    @PrepareForTest({SystemPropertyMockDemo.class})
    public class SystemPropertyMockDemoTest {
    @Test
    public void demoOfFinalSystemClassMocking() throws Exception {
    PowerMockito.mockStatic(System.class);
    PowerMockito.when(System.getProperty(“property”)).thenReturn(“my property”);
    PowerMock.replayAll();
    Assert.assertEquals(“my property”,
    new SystemPropertyMockDemo().getSystemProperty());
    PowerMock.verifyAll();
    }
    }
    注意:
    对于JDK的类如果要进行静态或final方法Mock时,@PrepareForTest()注解中只能放被测试的类,而非JDK的类,如上面例子中的SystemPropertyMockDemo.class。
    对于非JDK的类如果需要进行静态活final方法Mock时, @PrepareForTest()注解中直接放方法所在的类,若上面例子中的System不是JDK的类,则可以直接放System.class。
    @PrepareForTest({……}) 注解既可以加在类层次上(对整个测试文件有效),也可以加在测试方法上(只对测试方法有效)。
    (2).Mock非静态的final方法:
    被测试代码如下:
    public class ClassDependency {
    public final boolean isAlive() {
    return false;
    }
    }

    public class ClassUnderTest{
    public boolean callFinalMethod(ClassDependency refer) {
    return refer.isAlive();
    }
    }
    使用PowerMock的测试代码如下:
    @RunWith(PowerMockRunner.class)
    public class FinalMethodMockDemoTest {
    @Test
    @PrepareForTest(ClassDependency.class)
    public void testCallFinalMethod() {
    ClassDependency depencency = PowerMock.createMock(ClassDependency.class); //创建Mock对象
    ClassUnderTest underTest = new ClassUnderTest();
    EasyMock.expect(depencency.isAlive()).andReturn(true);
    PowerMock.replayAll();
    Assert.assertTrue(underTest.callFinalMethod(depencency));
    PowerMock.verifyAll();
    }
    }
    (3)部分Mock和私有方法Mock:
    如果被测试类某个方法不太容易调用,可以考虑只对该方法进行Mock,而其他方法全部使用被测试对象的真实方法,可以考虑使用PowerMock的部分Mock,被测试代码如下:
    public class DataService {
    public boolean replaceData(final String dataId, final byte[] binaryData) {
    return modifyData(dataId, binaryData);
    }
    public boolean deleteData(final String dataId) {
    return modifyData(dataId, null);
    }

        private boolean modifyData(final String dataId, final byte[] binaryData) {
                return true;
        }
    

    }
    只对modifyData方法进行Mock,而其他方法调用真实方法,测试代码如下:
    @RunWith(PowerMockRunner.class)
    @PrepareForTest(DataService.class)
    public class DataServiceTest {
    @Test
    public void testReplaceData() throws Exception {
    DataService tested = PowerMock.createPartialMock(DataService.class, “modifyData”);//创建部分mock对象,只对modifyData方法Mock
    PowerMock.expectPrivate(tested, “modifyData”, “id”, null).andReturn(true);//录制私有方法
    PowerMock.replay(tested);
    assertTrue(tested.deleteData(“id”));
    PowerMock.verify(tested);
    }
    }
    部分Mock在被测试方法的依赖在同一个类,且不容易创建时比较有用。
    个人认为私有方法的Mock意义不是很大,完全可以使用反射机制直接调用。
    (4).调用对象的构造方法Mock对象:
    在被测试方法内部调用构造创建了一个对象很常见,被测试代码如下:
    public class PersistenceManager {
    public boolean createDirectoryStructure(String directoryPath) {
    File directory = new File(directoryPath);
    if (directory.exists()) {
    throw new IllegalArgumentException(“\”” + directoryPath + “\” already exists.”);
    }
    return directory.mkdirs();
    }
    }
    创建文件操作(new File(path))依赖与操作系统底层实现,如果给定的路径不合法,将会出现异常导致测试无法正常覆盖,此时需要使用PowerMock的提供的调用构造方法创建Mock对象,测试代码如下:
    @RunWith(PowerMockRunner.class)
    @PrepareForTest( PersistenceManager.class )
    public class PersistenceManagerTest {
    @Test
    public void testCreateDirectoryStructure_ok() throws Exception {
    File fileMock = PowerMock.createMock(File.class);
    PersistenceManager tested = new PersistenceManager();
    PowerMock.expectNew(File.class, “directoryPath”).andReturn(fileMock);
    EasyMock.expect(fileMock.exists()).andReturn(false);
    EasyMock.expect(fileMock.mkdirs()).andReturn(true);
    PowerMock.replay(fileMock, File.class);
    assertTrue(tested.createDirectoryStructure(“directoryPath”));
    PowerMock.verify(fileMock, File.class);
    }
    }
    也可以使用更简便的方法:
    FilefileMock = PowerMock.createMockAndExpectNew(File.class,“directoryPath”);
    通过EasyMock+PowerMock,开发中绝大部分的方法都可以被测试完全覆盖。

    展开全文
  • JUnit是Java单元测试框架,已经在Eclipse中默认安装。目前主流的有JUnit3和JUnit4。JUnit3中,测试用例需要继承TestCase类。JUnit4中,测试用例无需继承TestCase类,只需要使用@Test等注解。先看一个Junit3的样例...
  • Java类静态方法的mock实现

    千次阅读 2022-02-02 23:49:03
    mock常见静态方法 待测试类: package com.taobao.vip.shop.biz.sqyk.manager; import com.taobao.vip.shop.biz.sqyk.util.TairUtil; /** * Created by yitian.zyt on 2021/6/30 */ @Slf4j @Component public...
  • Java-Mock简化单元测试

    2021-03-08 18:21:11
    通过Mock来解决对外部的依赖 Mockito的使用 基本使用 使用静态方法 mock() 使用注解 @Mock 标注 如果使用@Mock注解, 必须去触发所标注对象的创建. 可以使用 MockitoRule来实现. 它调用了静态方法MockitoAnnotations....
  • 为了让JAVA项目中的单元测试更加灵活便于编写,各种mock框架应运而生,其中最为常用和经典的mock框架非mockito与powermock莫属。 为了快速入门,本文将通过几个实例让大家快速了解mokito与powermock的使用方法。 ...
  • Java注解@Mock和@InjectMocks及@Mock和@Spy之间的区别 1.@Mock和@InjectMocks的区别 @Mock为您需要的类创建一个模拟实现。 @InjectMocks创建类的一个实例,并将用@Mock或@Spy注释创建的模拟注入到这个实例中。 注意...
  • 前段时间跟同学聊到这块的时候,他向我推荐mockito这个mock工具,试用了一下,确实很好用,这里给大家介绍下这款工具:1、mockito的特点它既能mock接口也能mock实体类(咱测试框架mock工具也能做到)简单的注解语法-...
  • 引入切面与注解 引入切面: @Bean public MethodMockAspect methodMockAspect() { return new MethodMockAspect(); } 复制代码 切面的扫描范围是添加了@MethodMock的类,这样类中的方法在调用过程中会被切面拦截 。 ...
  • 纯Mockito库import org.mockito.Mock;...@MockMyService myservice;和import org.mockito.Mockito;...MyService myservice = Mockito.mock(MyService.class);来自Mockito库,在功能上是等价的,它们允许模拟类或接口...
  • 在项目开发中,我们经常会使用@Value注解从配置文件中注入属性值,在编写单元测试时,在不启动容器的条件下,如何对这种属性进行mock呢?针对这种情况,Spring提供了一个很好的工具类ReflectionTestUtils来实现。...
  • 中的注释进行配置,各字段有详细解释; mockRequestDTOsList和mockResponseDTOS 都是List表示支持同一个方法的多组入参和出参匹配。 例如下面的配置: { ...
  • 基础知识储备-java-Java单元测试之Mock实战

    万次阅读 多人点赞 2018-09-05 09:40:48
    一、Mock的使用背景 单元测试的思路就是我们想在不涉及依赖关系的情况下测试代码。 在单元测试中,我们往往想去独立地去测一个类中的某个方法,但是这个类可不是独立的,它会去调用一些其它类的方法和service,这...
  • 借助feign-mock,您可以将预加载的JSON字符串或流用作响应的内容。 它还允许您验证模拟的调用,并且假冒伪装将打入您的注释以确保一切正常。 例子 private GitHub github; private MockClient mockClient; @...
  • 在介绍实际的例子之前,然我们先了解下什么是mock? 为什么mockmock的中文含义是假装模仿, 在单元测试里面我们测试的是某个单元的逻辑,即某个方法内的执行结果是否符合我们的预期。有一些方法会依赖于第三方的包...
  • @MockBean:@MockBeanMyService myservice;@嘲笑:@MockMyService myservice;Mockito.mock()MyService myservice = Mockito.mock(MyService.class);普通Mockito库import org.mockito.Mock;...@MockM...
  • 什么是Mock? 在面向对象程序设计中,模拟对象(英语:mock object,也译作模仿对象)是以可控的方式模拟真实对象行为的假的对象。程序员通常创造模拟对象(mock object)来测试其他对象的行为,很类似汽车设计者...
  • 预期回调接口生成期望值(直接执行) 修改对未预设的调用返回默认期望(指定返回值) 用spy监控真实对象,设置真实对象行为 不做任何返回 调用真实的方法 重置 mock @Mock 注解 指定测试类使用运行器:...
  • 有效使用Mock编写java单元测试

    万次阅读 多人点赞 2013-11-08 16:04:28
    Java单元测试对于开发人员质量保证至关重要,尤其当面对一团乱码的遗留代码时,没有高覆盖率的单元测试做保障,没人敢轻易对代码进行重构。然而单元测试的编写也不是一件容易的事情,除非使用TDD方式,否则编写出...
  • 解决方法 在测试类上增加注解 @SuppressStaticInitializationFor("org.mycompany.ClassWithEvilStaticInitializer") 里面填上你要mock类的包名+类名 参考资料 ... ...
  • java写单测过程中经常使用Mockito来为类中的属性和方法赋值,以简单高效的测试方法中逻辑的正确性。但遇到父类中存在引入变量时,初始化子类就会抛出空指针异常,导致整个单测报错,具体实例如下 public class A...
  • 解决方法: 根据我调试的内容,看起来Mockito无法通过注释处理类型参数.如果您遵循MockitoJunitRunner的堆栈跟踪,您最终会进入下面的DefaultAnnotationEngine是您要查看的方法. public void process(Class clazz, ...
  • in-view: false --- # 2个三横杠之间表示一个环境配置 spring: profiles: unittest # 单元测试,通过注解 ActiveProfiles 使用 datasource: url: jdbc:h2:mem:testdb driver-class-name: org.h2.Driver jpa: ...
  • 我们也将使用Type Signature注释获得相同的行为。 即使属性名称不同。 @RunWith(MockitoJUnitRunner.class) public class GameTest { @Mock Player player; @Spy List enemies = new ArrayList(); @InjectMocks ...
  • 我们为什么需要Mock? Mockito介绍 创建Mock对象 1.通过方法创建 2.通过注解创建 设置预期返回 Mockito测试案例
  • 深入探究@Mock注解的底层原理

    千次阅读 2019-10-11 21:44:14
    大公司里面测试是非常重要的,几乎占到了开发的二分之一公司,而Mockito框架是Java测试的首选,然后@Mock注解又是Mockito框架里面的重中之重,了解@Mock注解的原理对于Java的测试是非常有帮助的。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 16,104
精华内容 6,441
关键字:

mock注解java

java 订阅